diff --git a/crates/re_renderer/examples/2d.rs b/crates/re_renderer/examples/2d.rs index 4575d134f557..2523601da173 100644 --- a/crates/re_renderer/examples/2d.rs +++ b/crates/re_renderer/examples/2d.rs @@ -31,7 +31,7 @@ impl framework::Example for Render2D { let rerun_logo_texture = re_ctx .texture_manager_2d .create( - &mut re_ctx.gpu_resources.textures, + &re_ctx.gpu_resources.textures, &Texture2DCreationDesc { label: "rerun logo".into(), data: image_data.into(), diff --git a/crates/re_renderer/examples/depth_cloud.rs b/crates/re_renderer/examples/depth_cloud.rs index f67ae6e50089..d6b22ca5ed25 100644 --- a/crates/re_renderer/examples/depth_cloud.rs +++ b/crates/re_renderer/examples/depth_cloud.rs @@ -415,7 +415,7 @@ impl DepthTexture { .texture_manager_2d .get_or_create( hash(&label), - &mut re_ctx.gpu_resources.textures, + &re_ctx.gpu_resources.textures, Texture2DCreationDesc { label: label.into(), data: bytemuck::cast_slice(&data).into(), @@ -458,7 +458,7 @@ impl AlbedoTexture { .texture_manager_2d .get_or_create( hash(&label), - &mut re_ctx.gpu_resources.textures, + &re_ctx.gpu_resources.textures, Texture2DCreationDesc { label: label.into(), data: bytemuck::cast_slice(&rgba8).into(), diff --git a/crates/re_renderer/src/context.rs b/crates/re_renderer/src/context.rs index 048dc071ec53..baa1d788a454 100644 --- a/crates/re_renderer/src/context.rs +++ b/crates/re_renderer/src/context.rs @@ -180,7 +180,7 @@ impl RenderContext { &mut resolver, ))); let texture_manager_2d = - TextureManager2D::new(device.clone(), queue.clone(), &mut gpu_resources.textures); + TextureManager2D::new(device.clone(), queue.clone(), &gpu_resources.textures); let active_frame = ActiveFrameContext { before_view_builder_encoder: Mutex::new(FrameGlobalCommandEncoder::new(&device)), diff --git a/crates/re_renderer/src/importer/gltf.rs b/crates/re_renderer/src/importer/gltf.rs index 40ffcb9d382e..65ce55ff9693 100644 --- a/crates/re_renderer/src/importer/gltf.rs +++ b/crates/re_renderer/src/importer/gltf.rs @@ -19,7 +19,7 @@ pub fn load_gltf_from_buffer( mesh_name: &str, buffer: &[u8], lifetime: ResourceLifeTime, - ctx: &mut RenderContext, + ctx: &RenderContext, ) -> anyhow::Result> { re_tracing::profile_function!(); @@ -77,7 +77,7 @@ pub fn load_gltf_from_buffer( images_as_textures.push( match ctx .texture_manager_2d - .create(&mut ctx.gpu_resources.textures, &texture) + .create(&ctx.gpu_resources.textures, &texture) { Ok(texture) => texture, Err(err) => { @@ -92,13 +92,8 @@ pub fn load_gltf_from_buffer( for ref mesh in doc.meshes() { re_tracing::profile_scope!("mesh"); - let re_mesh = import_mesh( - mesh, - &buffers, - &images_as_textures, - &mut ctx.texture_manager_2d, - ) - .with_context(|| format!("mesh {} (name {:?})", mesh.index(), mesh.name()))?; + let re_mesh = import_mesh(mesh, &buffers, &images_as_textures, &ctx.texture_manager_2d) + .with_context(|| format!("mesh {} (name {:?})", mesh.index(), mesh.name()))?; meshes.insert( mesh.index(), ( @@ -143,7 +138,7 @@ fn import_mesh( mesh: &gltf::Mesh<'_>, buffers: &[gltf::buffer::Data], gpu_image_handles: &[GpuTexture2D], - texture_manager: &mut TextureManager2D, //imported_materials: HashMap, + texture_manager: &TextureManager2D, //imported_materials: HashMap, ) -> anyhow::Result { re_tracing::profile_function!(); diff --git a/crates/re_renderer/src/resource_managers/texture_manager.rs b/crates/re_renderer/src/resource_managers/texture_manager.rs index 5a9469d7170b..c9464b10afd1 100644 --- a/crates/re_renderer/src/resource_managers/texture_manager.rs +++ b/crates/re_renderer/src/resource_managers/texture_manager.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use ahash::{HashMap, HashSet}; +use parking_lot::Mutex; use crate::{ wgpu_resources::{GpuTexture, GpuTexturePool, TextureDesc}, @@ -128,6 +129,8 @@ impl From> for TextureCreationError { /// More complex textures types are typically handled within renderer which utilize the texture pool directly. /// This manager in contrast, deals with user provided texture data! /// We might revisit this later and make this texture manager more general purpose. +/// +/// Has intertior mutability. pub struct TextureManager2D { // Long lived/short lived doesn't make sense for textures since we don't yet know a way to // optimize for short lived textures as we do with buffer data. @@ -142,6 +145,12 @@ pub struct TextureManager2D { device: Arc, queue: Arc, + /// The mutable part of the manager. + inner: Mutex, +} + +#[derive(Default)] +struct Inner { // Cache the texture using a user-provided u64 id. This is expected // to be derived from the `TensorId` which isn't available here for // dependency reasons. @@ -153,11 +162,20 @@ pub struct TextureManager2D { accessed_textures: HashSet, } +impl Inner { + fn begin_frame(&mut self, _frame_index: u64) { + // Drop any textures that weren't accessed in the last frame + self.texture_cache + .retain(|k, _| self.accessed_textures.contains(k)); + self.accessed_textures.clear(); + } +} + impl TextureManager2D { pub(crate) fn new( device: Arc, queue: Arc, - texture_pool: &mut GpuTexturePool, + texture_pool: &GpuTexturePool, ) -> Self { re_tracing::profile_function!(); @@ -192,16 +210,15 @@ impl TextureManager2D { zeroed_texture_uint, device, queue, - texture_cache: Default::default(), - accessed_textures: Default::default(), + inner: Default::default(), } } /// Creates a new 2D texture resource and schedules data upload to the GPU. /// TODO(jleibs): All usages of this should be be replaced with `get_or_create`, which is strictly preferable pub fn create( - &mut self, - texture_pool: &mut GpuTexturePool, + &self, + texture_pool: &GpuTexturePool, creation_desc: &Texture2DCreationDesc<'_>, ) -> Result { // TODO(andreas): Disabled the warning as we're moving towards using this texture manager for user-logged images. @@ -226,9 +243,9 @@ impl TextureManager2D { /// Creates a new 2D texture resource and schedules data upload to the GPU if a texture /// wasn't already created using the same key. pub fn get_or_create( - &mut self, + &self, key: u64, - texture_pool: &mut GpuTexturePool, + texture_pool: &GpuTexturePool, texture_desc: Texture2DCreationDesc<'_>, ) -> Result { self.get_or_create_with(key, texture_pool, || texture_desc) @@ -237,9 +254,9 @@ impl TextureManager2D { /// Creates a new 2D texture resource and schedules data upload to the GPU if a texture /// wasn't already created using the same key. pub fn get_or_create_with<'a>( - &mut self, + &self, key: u64, - texture_pool: &mut GpuTexturePool, + texture_pool: &GpuTexturePool, create_texture_desc: impl FnOnce() -> Texture2DCreationDesc<'a>, ) -> Result { self.get_or_try_create_with(key, texture_pool, || -> Result<_, never::Never> { @@ -251,12 +268,13 @@ impl TextureManager2D { /// Creates a new 2D texture resource and schedules data upload to the GPU if a texture /// wasn't already created using the same key. pub fn get_or_try_create_with<'a, Err: std::fmt::Display>( - &mut self, + &self, key: u64, - texture_pool: &mut GpuTexturePool, + texture_pool: &GpuTexturePool, try_create_texture_desc: impl FnOnce() -> Result, Err>, ) -> Result> { - let texture_handle = match self.texture_cache.entry(key) { + let mut inner = self.inner.lock(); + let texture_handle = match inner.texture_cache.entry(key) { std::collections::hash_map::Entry::Occupied(texture_handle) => { texture_handle.get().clone() // already inserted } @@ -274,7 +292,7 @@ impl TextureManager2D { } }; - self.accessed_textures.insert(key); + inner.accessed_textures.insert(key); Ok(texture_handle) } @@ -311,7 +329,7 @@ impl TextureManager2D { fn create_and_upload_texture( device: &wgpu::Device, queue: &wgpu::Queue, - texture_pool: &mut GpuTexturePool, + texture_pool: &GpuTexturePool, creation_desc: &Texture2DCreationDesc<'_>, ) -> Result { re_tracing::profile_function!(); @@ -376,16 +394,13 @@ impl TextureManager2D { Ok(GpuTexture2D(texture)) } - pub(crate) fn begin_frame(&mut self, _frame_index: u64) { - // Drop any textures that weren't accessed in the last frame - self.texture_cache - .retain(|k, _| self.accessed_textures.contains(k)); - self.accessed_textures.clear(); + pub(crate) fn begin_frame(&self, _frame_index: u64) { + self.inner.lock().begin_frame(_frame_index); } } fn create_zero_texture( - texture_pool: &mut GpuTexturePool, + texture_pool: &GpuTexturePool, device: &Arc, format: wgpu::TextureFormat, ) -> GpuTexture2D { diff --git a/crates/re_space_view_spatial/src/mesh_cache.rs b/crates/re_space_view_spatial/src/mesh_cache.rs index 6a1124fe921b..1e33b36349d8 100644 --- a/crates/re_space_view_spatial/src/mesh_cache.rs +++ b/crates/re_space_view_spatial/src/mesh_cache.rs @@ -16,7 +16,7 @@ impl MeshCache { &mut self, name: &str, mesh: &Mesh3D, - render_ctx: &mut RenderContext, + render_ctx: &RenderContext, ) -> Option> { re_tracing::profile_function!(); diff --git a/crates/re_space_view_spatial/src/mesh_loader.rs b/crates/re_space_view_spatial/src/mesh_loader.rs index 20789c5431dc..252fbadd326e 100644 --- a/crates/re_space_view_spatial/src/mesh_loader.rs +++ b/crates/re_space_view_spatial/src/mesh_loader.rs @@ -12,11 +12,7 @@ pub struct LoadedMesh { } impl LoadedMesh { - pub fn load( - name: String, - mesh: &Mesh3D, - render_ctx: &mut RenderContext, - ) -> anyhow::Result { + pub fn load(name: String, mesh: &Mesh3D, render_ctx: &RenderContext) -> anyhow::Result { // TODO(emilk): load CpuMesh in background thread. match mesh { // Mesh from some file format. File passed in bytes. @@ -32,7 +28,7 @@ impl LoadedMesh { name: String, format: MeshFormat, bytes: &[u8], - render_ctx: &mut RenderContext, + render_ctx: &RenderContext, ) -> anyhow::Result { re_tracing::profile_function!(); @@ -60,7 +56,7 @@ impl LoadedMesh { fn load_encoded_mesh( name: String, encoded_mesh: &EncodedMesh3D, - render_ctx: &mut RenderContext, + render_ctx: &RenderContext, ) -> anyhow::Result { re_tracing::profile_function!(); let EncodedMesh3D { @@ -88,7 +84,7 @@ impl LoadedMesh { fn load_raw_mesh( name: String, raw_mesh: &RawMesh3D, - render_ctx: &mut RenderContext, + render_ctx: &RenderContext, ) -> anyhow::Result { re_tracing::profile_function!(); diff --git a/crates/re_space_view_spatial/src/scene/parts/images.rs b/crates/re_space_view_spatial/src/scene/parts/images.rs index 4fef569441fd..a3dac3e2cb03 100644 --- a/crates/re_space_view_spatial/src/scene/parts/images.rs +++ b/crates/re_space_view_spatial/src/scene/parts/images.rs @@ -323,35 +323,31 @@ impl ImagesPart { let mut data_f32 = Vec::new(); ctx.render_ctx .texture_manager_2d - .get_or_try_create_with( - texture_key, - &mut ctx.render_ctx.gpu_resources.textures, - || { - // TODO(andreas/cmc): Ideally we'd upload the u16 data as-is. - // However, R16Unorm is behind a feature flag and Depth16Unorm doesn't work on WebGL (and is awkward as this is a depth buffer format!). - let data = match &tensor.data { - TensorData::U16(data) => { - data_f32.extend(data.as_slice().iter().map(|d| *d as f32)); - bytemuck::cast_slice(&data_f32).into() - } - TensorData::F32(data) => bytemuck::cast_slice(data).into(), - _ => { - return Err(format!( - "Tensor datatype {} is not supported for back-projection", - tensor.dtype() - )); - } - }; - - Ok(Texture2DCreationDesc { - label: format!("Depth cloud for {ent_path:?}").into(), - data, - format: wgpu::TextureFormat::R32Float, - width: width as _, - height: height as _, - }) - }, - ) + .get_or_try_create_with(texture_key, &ctx.render_ctx.gpu_resources.textures, || { + // TODO(andreas/cmc): Ideally we'd upload the u16 data as-is. + // However, R16Unorm is behind a feature flag and Depth16Unorm doesn't work on WebGL (and is awkward as this is a depth buffer format!). + let data = match &tensor.data { + TensorData::U16(data) => { + data_f32.extend(data.as_slice().iter().map(|d| *d as f32)); + bytemuck::cast_slice(&data_f32).into() + } + TensorData::F32(data) => bytemuck::cast_slice(data).into(), + _ => { + return Err(format!( + "Tensor datatype {} is not supported for back-projection", + tensor.dtype() + )); + } + }; + + Ok(Texture2DCreationDesc { + label: format!("Depth cloud for {ent_path:?}").into(), + data, + format: wgpu::TextureFormat::R32Float, + width: width as _, + height: height as _, + }) + }) .map_err(|err| format!("Failed to create depth cloud texture: {err}"))? }; diff --git a/crates/re_viewer_context/src/gpu_bridge/mod.rs b/crates/re_viewer_context/src/gpu_bridge/mod.rs index 1e53ce229ecf..03ec3dfd55a1 100644 --- a/crates/re_viewer_context/src/gpu_bridge/mod.rs +++ b/crates/re_viewer_context/src/gpu_bridge/mod.rs @@ -57,25 +57,25 @@ pub fn viewport_resolution_in_pixels(clip_rect: egui::Rect, pixels_from_point: f } pub fn try_get_or_create_texture<'a, Err: std::fmt::Display>( - render_ctx: &mut RenderContext, + render_ctx: &RenderContext, texture_key: u64, try_create_texture_desc: impl FnOnce() -> Result, Err>, ) -> Result> { render_ctx.texture_manager_2d.get_or_try_create_with( texture_key, - &mut render_ctx.gpu_resources.textures, + &render_ctx.gpu_resources.textures, try_create_texture_desc, ) } pub fn get_or_create_texture<'a>( - render_ctx: &mut RenderContext, + render_ctx: &RenderContext, texture_key: u64, create_texture_desc: impl FnOnce() -> Texture2DCreationDesc<'a>, ) -> Result { render_ctx.texture_manager_2d.get_or_create_with( texture_key, - &mut render_ctx.gpu_resources.textures, + &render_ctx.gpu_resources.textures, create_texture_desc, ) } 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 68765af83fff..636bd9b20908 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 @@ -29,7 +29,7 @@ use super::{get_or_create_texture, try_get_or_create_texture}; /// `tensor_stats` is used for determining the range of the texture. // TODO(emilk): allow user to specify the range in ui. pub fn tensor_to_gpu( - render_ctx: &mut RenderContext, + render_ctx: &RenderContext, debug_name: &str, tensor: &DecodedTensor, tensor_stats: &TensorStats, @@ -61,7 +61,7 @@ pub fn tensor_to_gpu( // Color textures: fn color_tensor_to_gpu( - render_ctx: &mut RenderContext, + render_ctx: &RenderContext, debug_name: &str, tensor: &DecodedTensor, tensor_stats: &TensorStats, @@ -131,7 +131,7 @@ fn color_tensor_to_gpu( // Textures with class_id annotations: fn class_id_tensor_to_gpu( - render_ctx: &mut RenderContext, + render_ctx: &RenderContext, debug_name: &str, tensor: &DecodedTensor, tensor_stats: &TensorStats, @@ -201,7 +201,7 @@ fn class_id_tensor_to_gpu( // Depth textures: fn depth_tensor_to_gpu( - render_ctx: &mut RenderContext, + render_ctx: &RenderContext, debug_name: &str, tensor: &DecodedTensor, tensor_stats: &TensorStats,