Sfoglia il codice sorgente

app: introduce debug tags (zero-cost) for gfx calls render subsystem which improves the debugging capability when an error occurs

darkfi 1 anno fa
parent
commit
ebd82a3d0b

+ 12 - 0
bin/app/src/error.rs

@@ -124,6 +124,18 @@ pub enum Error {
 
     #[error("Service failed")]
     ServiceFailed = 40,
+
+    #[error("Duplicate texture ID")]
+    GfxDuplicateTextureID = 41,
+
+    #[error("Unknown texture ID")]
+    GfxUnknownTextureID = 42,
+
+    #[error("Duplicate buffer ID")]
+    GfxDuplicateBufferID = 43,
+
+    #[error("Unknown buffer ID")]
+    GfxUnknownBufferID = 44,
 }
 
 impl From<sled::Error> for Error {

+ 107 - 36
bin/app/src/gfx/mod.rs

@@ -54,6 +54,22 @@ use crate::{
 const DEBUG_RENDER: bool = false;
 const DEBUG_GFXAPI: bool = false;
 
+#[macro_export]
+macro_rules! gfxtag {
+    ($s:expr) => {{
+        Some($s)
+    }};
+}
+pub use crate::gfxtag;
+
+pub type DebugTag = Option<&'static str>;
+
+macro_rules! t { ($($arg:tt)*) => { trace!(target: "gfx", $($arg)*); } }
+macro_rules! d { ($($arg:tt)*) => { debug!(target: "gfx", $($arg)*); } }
+macro_rules! i { ($($arg:tt)*) => { info!(target: "gfx", $($arg)*); } }
+macro_rules! e { ($($arg:tt)*) => { error!(target: "gfx", $($arg)*); } }
+macro_rules! w { ($($arg:tt)*) => { warn!(target: "gfx", $($arg)*); } }
+
 #[cfg(target_os = "android")]
 pub fn get_window_size_filename() -> PathBuf {
     crate::android::get_appdata_path().join("window_size")
@@ -93,11 +109,12 @@ pub type ManagedTexturePtr = Arc<ManagedTexture>;
 pub struct ManagedTexture {
     id: GfxTextureId,
     render_api: RenderApi,
+    tag: DebugTag,
 }
 
 impl Drop for ManagedTexture {
     fn drop(&mut self) {
-        self.render_api.delete_unmanaged_texture(self.id);
+        self.render_api.delete_unmanaged_texture(self.id, self.tag);
     }
 }
 
@@ -114,11 +131,13 @@ pub type ManagedBufferPtr = Arc<ManagedBuffer>;
 pub struct ManagedBuffer {
     id: GfxBufferId,
     render_api: RenderApi,
+    tag: DebugTag,
+    buftype: u8,
 }
 
 impl Drop for ManagedBuffer {
     fn drop(&mut self) {
-        self.render_api.delete_unmanaged_buffer(self.id);
+        self.render_api.delete_unmanaged_buffer(self.id, self.tag, self.buftype);
     }
 }
 
@@ -161,15 +180,22 @@ impl RenderApi {
         gfx_texture_id
     }
 
-    pub fn new_texture(&self, width: u16, height: u16, data: Vec<u8>) -> ManagedTexturePtr {
+    pub fn new_texture(
+        &self,
+        width: u16,
+        height: u16,
+        data: Vec<u8>,
+        tag: DebugTag,
+    ) -> ManagedTexturePtr {
         Arc::new(ManagedTexture {
             id: self.new_unmanaged_texture(width, height, data),
             render_api: self.clone(),
+            tag,
         })
     }
 
-    fn delete_unmanaged_texture(&self, texture: GfxTextureId) {
-        let method = GraphicsMethod::DeleteTexture(texture);
+    fn delete_unmanaged_texture(&self, texture: GfxTextureId, tag: DebugTag) {
+        let method = GraphicsMethod::DeleteTexture((texture, tag));
         self.send(method);
     }
 
@@ -191,21 +217,25 @@ impl RenderApi {
         gfx_buffer_id
     }
 
-    pub fn new_vertex_buffer(&self, verts: Vec<Vertex>) -> ManagedBufferPtr {
+    pub fn new_vertex_buffer(&self, verts: Vec<Vertex>, tag: DebugTag) -> ManagedBufferPtr {
         Arc::new(ManagedBuffer {
             id: self.new_unmanaged_vertex_buffer(verts),
             render_api: self.clone(),
+            tag,
+            buftype: 0,
         })
     }
-    pub fn new_index_buffer(&self, indices: Vec<u16>) -> ManagedBufferPtr {
+    pub fn new_index_buffer(&self, indices: Vec<u16>, tag: DebugTag) -> ManagedBufferPtr {
         Arc::new(ManagedBuffer {
             id: self.new_unmanaged_index_buffer(indices),
             render_api: self.clone(),
+            tag,
+            buftype: 1,
         })
     }
 
-    fn delete_unmanaged_buffer(&self, buffer: GfxBufferId) {
-        let method = GraphicsMethod::DeleteBuffer(buffer);
+    fn delete_unmanaged_buffer(&self, buffer: GfxBufferId, tag: DebugTag, buftype: u8) {
+        let method = GraphicsMethod::DeleteBuffer((buffer, tag, buftype));
         self.send(method);
     }
 
@@ -536,10 +566,10 @@ impl<'a> RenderContext<'a> {
 #[derive(Clone, Debug)]
 pub enum GraphicsMethod {
     NewTexture((u16, u16, Vec<u8>, GfxTextureId)),
-    DeleteTexture(GfxTextureId),
+    DeleteTexture((GfxTextureId, DebugTag)),
     NewVertexBuffer((Vec<Vertex>, GfxBufferId)),
     NewIndexBuffer((Vec<u16>, GfxBufferId)),
-    DeleteBuffer(GfxBufferId),
+    DeleteBuffer((GfxBufferId, DebugTag, u8)),
     ReplaceDrawCalls { timest: u64, dcs: Vec<(u64, GfxDrawCall)> },
 }
 
@@ -723,34 +753,45 @@ impl Stage {
         }
     }
 
-    fn process_method(&mut self, method: GraphicsMethod) {
+    fn clear(&mut self) {
+        std::mem::take(&mut self.draw_calls);
+        std::mem::take(&mut self.textures);
+        std::mem::take(&mut self.buffers);
+    }
+
+    fn process_method(&mut self, mut method: GraphicsMethod) {
         //debug!(target: "gfx", "Received method: {:?}", method);
-        match method {
+        let res = match &mut method {
             GraphicsMethod::NewTexture((width, height, data, gfx_texture_id)) => {
-                self.method_new_texture(width, height, data, gfx_texture_id)
+                self.method_new_texture(*width, *height, data, *gfx_texture_id)
             }
-            GraphicsMethod::DeleteTexture(texture) => self.method_delete_texture(texture),
-            GraphicsMethod::NewVertexBuffer((verts, sendr)) => {
-                self.method_new_vertex_buffer(verts, sendr)
+            GraphicsMethod::DeleteTexture((texture, _)) => self.method_delete_texture(*texture),
+            GraphicsMethod::NewVertexBuffer((verts, gbuffid)) => {
+                self.method_new_vertex_buffer(verts, *gbuffid)
             }
-            GraphicsMethod::NewIndexBuffer((indices, sendr)) => {
-                self.method_new_index_buffer(indices, sendr)
+            GraphicsMethod::NewIndexBuffer((indices, gbuffid)) => {
+                self.method_new_index_buffer(indices, *gbuffid)
             }
-            GraphicsMethod::DeleteBuffer(buffer) => self.method_delete_buffer(buffer),
+            GraphicsMethod::DeleteBuffer((buffer, _, _)) => self.method_delete_buffer(*buffer),
             GraphicsMethod::ReplaceDrawCalls { timest, dcs } => {
-                self.method_replace_draw_calls(timest, dcs)
+                let dcs = std::mem::take(dcs);
+                self.method_replace_draw_calls(*timest, dcs)
             }
         };
+        if let Err(err) = res {
+            e!("process_method(method={method:?}) failed with err: {err:?}");
+            panic!("process_method failed!")
+        }
     }
 
     fn method_new_texture(
         &mut self,
         width: u16,
         height: u16,
-        data: Vec<u8>,
+        data: &Vec<u8>,
         gfx_texture_id: GfxTextureId,
-    ) {
-        let texture = self.ctx.new_texture_from_rgba8(width, height, &data);
+    ) -> Result<()> {
+        let texture = self.ctx.new_texture_from_rgba8(width, height, data);
         if DEBUG_GFXAPI {
             debug!(target: "gfx", "Invoked method: new_texture({}, {}, ..., {}) -> {:?}",
                    width, height, gfx_texture_id, texture);
@@ -759,22 +800,32 @@ impl Stage {
             //       ansi_texture(width as usize, height as usize, &data));
         }
         if let Some(_) = self.textures.insert(gfx_texture_id, texture) {
-            panic!("Duplicate texture ID={gfx_texture_id} detected!");
+            //panic!("Duplicate texture ID={gfx_texture_id} detected!");
+            return Err(Error::GfxDuplicateTextureID)
         }
+        Ok(())
     }
-    fn method_delete_texture(&mut self, gfx_texture_id: GfxTextureId) {
-        let texture = self.textures.remove(&gfx_texture_id).expect("couldn't find gfx_texture_id");
+    fn method_delete_texture(&mut self, gfx_texture_id: GfxTextureId) -> Result<()> {
+        let Some(texture) = self.textures.remove(&gfx_texture_id) else {
+            //.expect("couldn't find gfx_texture_id");
+            return Err(Error::GfxUnknownTextureID)
+        };
         if DEBUG_GFXAPI {
             debug!(target: "gfx", "Invoked method: delete_texture({} => {:?})",
                    gfx_texture_id, texture);
         }
         self.ctx.delete_texture(texture);
+        Ok(())
     }
-    fn method_new_vertex_buffer(&mut self, verts: Vec<Vertex>, gfx_buffer_id: GfxBufferId) {
+    fn method_new_vertex_buffer(
+        &mut self,
+        verts: &[Vertex],
+        gfx_buffer_id: GfxBufferId,
+    ) -> Result<()> {
         let buffer = self.ctx.new_buffer(
             BufferType::VertexBuffer,
             BufferUsage::Immutable,
-            BufferSource::slice(&verts),
+            BufferSource::slice(verts),
         );
         if DEBUG_GFXAPI {
             debug!(target: "gfx", "Invoked method: new_vertex_buffer(..., {}) -> {:?}",
@@ -783,10 +834,16 @@ impl Stage {
             //       verts, gfx_buffer_id, buffer);
         }
         if let Some(_) = self.buffers.insert(gfx_buffer_id, buffer) {
-            panic!("Duplicate vertex buffer ID={gfx_buffer_id} detected!");
+            //panic!("Duplicate vertex buffer ID={gfx_buffer_id} detected!");
+            return Err(Error::GfxDuplicateBufferID)
         }
+        Ok(())
     }
-    fn method_new_index_buffer(&mut self, indices: Vec<u16>, gfx_buffer_id: GfxBufferId) {
+    fn method_new_index_buffer(
+        &mut self,
+        indices: &[u16],
+        gfx_buffer_id: GfxBufferId,
+    ) -> Result<()> {
         let buffer = self.ctx.new_buffer(
             BufferType::IndexBuffer,
             BufferUsage::Immutable,
@@ -799,18 +856,28 @@ impl Stage {
             //       indices, gfx_buffer_id, buffer);
         }
         if let Some(_) = self.buffers.insert(gfx_buffer_id, buffer) {
-            panic!("Duplicate index buffer ID={gfx_buffer_id} detected!");
+            //panic!("Duplicate index buffer ID={gfx_buffer_id} detected!");
+            return Err(Error::GfxDuplicateBufferID)
         }
+        Ok(())
     }
-    fn method_delete_buffer(&mut self, gfx_buffer_id: GfxBufferId) {
-        let buffer = self.buffers.remove(&gfx_buffer_id).expect("couldn't find gfx_buffer_id");
+    fn method_delete_buffer(&mut self, gfx_buffer_id: GfxBufferId) -> Result<()> {
+        let Some(buffer) = self.buffers.remove(&gfx_buffer_id) else {
+            //.expect("couldn't find gfx_buffer_id");
+            return Err(Error::GfxUnknownBufferID)
+        };
         if DEBUG_GFXAPI {
             debug!(target: "gfx", "Invoked method: delete_buffer({} => {:?})",
                    gfx_buffer_id, buffer);
         }
         self.ctx.delete_buffer(buffer);
+        Ok(())
     }
-    fn method_replace_draw_calls(&mut self, timest: u64, dcs: Vec<(u64, GfxDrawCall)>) {
+    fn method_replace_draw_calls(
+        &mut self,
+        timest: u64,
+        dcs: Vec<(u64, GfxDrawCall)>,
+    ) -> Result<()> {
         if DEBUG_GFXAPI {
             debug!(target: "gfx", "Invoked method: replace_draw_calls({:?})", dcs);
         }
@@ -834,6 +901,7 @@ impl Stage {
                 }
             }
         }
+        Ok(())
     }
 }
 
@@ -938,7 +1006,10 @@ impl EventHandler for Stage {
     fn force_reload(&mut self) {
         let god = GOD.get().unwrap();
         god.stop_app();
-        god.start_app();
+        //god.start_app();
+        drop(god);
+        self.clear();
+        *self = Self::new();
     }
 }
 

+ 11 - 7
bin/app/src/mesh.rs

@@ -18,7 +18,10 @@
 
 use crate::{
     error::Result,
-    gfx::{GfxDrawMesh, ManagedBufferPtr, ManagedTexturePtr, Point, Rectangle, RenderApi, Vertex},
+    gfx::{
+        DebugTag, GfxDrawMesh, ManagedBufferPtr, ManagedTexturePtr, Point, Rectangle, RenderApi,
+        Vertex,
+    },
 };
 
 pub type Color = [f32; 4];
@@ -70,14 +73,15 @@ pub struct MeshBuilder {
     pub verts: Vec<Vertex>,
     pub indices: Vec<u16>,
     clipper: Option<Rectangle>,
+    tag: DebugTag,
 }
 
 impl MeshBuilder {
-    pub fn new() -> Self {
-        Self { verts: vec![], indices: vec![], clipper: None }
+    pub fn new(tag: DebugTag) -> Self {
+        Self { verts: vec![], indices: vec![], clipper: None, tag }
     }
-    pub fn with_clip(clipper: Rectangle) -> Self {
-        Self { verts: vec![], indices: vec![], clipper: Some(clipper) }
+    pub fn with_clip(tag: DebugTag, clipper: Rectangle) -> Self {
+        Self { verts: vec![], indices: vec![], clipper: Some(clipper), tag }
     }
 
     pub fn append(&mut self, mut verts: Vec<Vertex>, indices: Vec<u16>) {
@@ -187,8 +191,8 @@ impl MeshBuilder {
         //    debug!(target: "mesh", "  {:?}", vert);
         //}
         let num_elements = self.indices.len() as i32;
-        let vertex_buffer = render_api.new_vertex_buffer(self.verts);
-        let index_buffer = render_api.new_index_buffer(self.indices);
+        let vertex_buffer = render_api.new_vertex_buffer(self.verts, self.tag);
+        let index_buffer = render_api.new_index_buffer(self.indices, self.tag);
         MeshInfo { vertex_buffer, index_buffer, num_elements }
     }
 }

+ 2 - 2
bin/app/src/plugin/darkirc.rs

@@ -211,13 +211,13 @@ impl DarkIrc {
 
             p2p_settings.seeds.push(
                 url::Url::parse(
-                    "tor://g7fxelebievvpr27w7gt24lflptpw3jeeuvafovgliq5utdst6xyruyd.onion:25552"
+                    "tor://g7fxelebievvpr27w7gt24lflptpw3jeeuvafovgliq5utdst6xyruyd.onion:25552",
                 )
                 .unwrap(),
             );
             p2p_settings.seeds.push(
                 url::Url::parse(
-                    "tor://yvklzjnfmwxhyodhrkpomawjcdvcaushsj6torjz2gyd7e25f3gfunyd.onion:25552"
+                    "tor://yvklzjnfmwxhyodhrkpomawjcdvcaushsj6torjz2gyd7e25f3gfunyd.onion:25552",
                 )
                 .unwrap(),
             );

+ 12 - 5
bin/app/src/text/atlas.rs

@@ -18,7 +18,7 @@
 
 use crate::{
     error::Result,
-    gfx::{GfxTextureId, ManagedTexturePtr, Rectangle, RenderApi},
+    gfx::{DebugTag, GfxTextureId, ManagedTexturePtr, Rectangle, RenderApi},
 };
 
 use super::{
@@ -32,8 +32,12 @@ use super::{
 const ATLAS_GAP: usize = 2;
 
 /// Convenience wrapper fn. Use if rendering a single line of glyphs.
-pub fn make_texture_atlas(render_api: &RenderApi, glyphs: &Vec<Glyph>) -> RenderedAtlas {
-    let mut atlas = Atlas::new(render_api);
+pub fn make_texture_atlas(
+    render_api: &RenderApi,
+    tag: DebugTag,
+    glyphs: &Vec<Glyph>,
+) -> RenderedAtlas {
+    let mut atlas = Atlas::new(render_api, tag);
     atlas.push(&glyphs);
     atlas.make()
 }
@@ -60,10 +64,11 @@ pub struct Atlas<'a> {
     height: usize,
 
     render_api: &'a RenderApi,
+    tag: DebugTag,
 }
 
 impl<'a> Atlas<'a> {
-    pub fn new(render_api: &'a RenderApi) -> Self {
+    pub fn new(render_api: &'a RenderApi, tag: DebugTag) -> Self {
         Self {
             glyph_ids: vec![],
             sprites: vec![],
@@ -74,6 +79,7 @@ impl<'a> Atlas<'a> {
             // FYI glyphs have a gap on all sides (top and bottom here).
             height: 2 * ATLAS_GAP,
             render_api,
+            tag,
         }
     }
 
@@ -160,7 +166,8 @@ impl<'a> Atlas<'a> {
         assert_eq!(self.glyph_ids.len(), self.x_pos.len());
 
         let atlas = self.render();
-        let texture = self.render_api.new_texture(self.width as u16, self.height as u16, atlas);
+        let texture =
+            self.render_api.new_texture(self.width as u16, self.height as u16, atlas, self.tag);
 
         let uv_rects = self.compute_uvs();
         let glyph_ids = self.glyph_ids;

+ 6 - 3
bin/app/src/text2/atlas.rs

@@ -18,7 +18,7 @@
 
 use crate::{
     error::Result,
-    gfx::{GfxTextureId, ManagedTexturePtr, Rectangle, RenderApi},
+    gfx::{DebugTag, GfxTextureId, ManagedTexturePtr, Rectangle, RenderApi},
     mesh::Color,
 };
 
@@ -60,10 +60,11 @@ pub struct Atlas<'a> {
     height: usize,
 
     render_api: &'a RenderApi,
+    tag: DebugTag,
 }
 
 impl<'a> Atlas<'a> {
-    pub fn new(scaler: swash::scale::Scaler<'a>, render_api: &'a RenderApi) -> Self {
+    pub fn new(scaler: swash::scale::Scaler<'a>, render_api: &'a RenderApi, tag: DebugTag) -> Self {
         Self {
             scaler,
             glyph_ids: vec![],
@@ -77,6 +78,7 @@ impl<'a> Atlas<'a> {
             height: 2 * ATLAS_GAP,
 
             render_api,
+            tag,
         }
     }
 
@@ -178,7 +180,8 @@ impl<'a> Atlas<'a> {
         assert_eq!(self.glyph_ids.len(), self.x_pos.len());
 
         let atlas = self.render();
-        let texture = self.render_api.new_texture(self.width as u16, self.height as u16, atlas);
+        let texture =
+            self.render_api.new_texture(self.width as u16, self.height as u16, atlas, self.tag);
 
         let uv_rects = self.compute_uvs();
         let glyph_ids = self.glyph_ids;

+ 10 - 6
bin/app/src/text2/render.rs

@@ -17,7 +17,7 @@
  */
 
 use crate::{
-    gfx::{GfxDrawInstruction, GfxDrawMesh, Point, Rectangle, RenderApi},
+    gfx::{DebugTag, GfxDrawInstruction, GfxDrawMesh, Point, Rectangle, RenderApi},
     mesh::{Color, MeshBuilder, COLOR_WHITE},
 };
 
@@ -52,14 +52,16 @@ impl std::ops::BitOrAssign for DebugRenderOptions {
 pub fn render_layout(
     layout: &parley::Layout<Color>,
     render_api: &RenderApi,
+    tag: DebugTag,
 ) -> Vec<GfxDrawInstruction> {
-    render_layout_with_opts(layout, DebugRenderOptions::Off, render_api)
+    render_layout_with_opts(layout, DebugRenderOptions::Off, render_api, tag)
 }
 
 pub fn render_layout_with_opts(
     layout: &parley::Layout<Color>,
     opts: DebugRenderOptions,
     render_api: &RenderApi,
+    tag: DebugTag,
 ) -> Vec<GfxDrawInstruction> {
     let mut scale_cx = swash::scale::ScaleContext::new();
     let mut run_idx = 0;
@@ -69,7 +71,7 @@ pub fn render_layout_with_opts(
             match item {
                 parley::PositionedLayoutItem::GlyphRun(glyph_run) => {
                     let mesh =
-                        render_glyph_run(&mut scale_cx, &glyph_run, run_idx, opts, render_api);
+                        render_glyph_run(&mut scale_cx, &glyph_run, run_idx, opts, render_api, tag);
                     instrs.push(GfxDrawInstruction::Draw(mesh));
                     run_idx += 1;
                 }
@@ -86,6 +88,7 @@ fn render_glyph_run(
     run_idx: usize,
     opts: DebugRenderOptions,
     render_api: &RenderApi,
+    tag: DebugTag,
 ) -> GfxDrawMesh {
     let mut run_x = glyph_run.offset();
     let run_y = glyph_run.baseline();
@@ -93,9 +96,9 @@ fn render_glyph_run(
     let color = style.brush;
     //trace!(target: "text::render", "render_glyph_run run_idx={run_idx} baseline={run_y}");
 
-    let atlas = create_atlas(scale_ctx, glyph_run, render_api);
+    let atlas = create_atlas(scale_ctx, glyph_run, render_api, tag);
 
-    let mut mesh = MeshBuilder::new();
+    let mut mesh = MeshBuilder::new(tag);
 
     if let Some(underline) = &style.underline {
         render_underline(underline, glyph_run, &mut mesh);
@@ -168,6 +171,7 @@ fn create_atlas(
     scale_ctx: &mut swash::scale::ScaleContext,
     glyph_run: &parley::GlyphRun<'_, Color>,
     render_api: &RenderApi,
+    tag: DebugTag,
 ) -> RenderedAtlas {
     let run = glyph_run.run();
     let font = run.font();
@@ -182,7 +186,7 @@ fn create_atlas(
         .normalized_coords(normalized_coords)
         .build();
 
-    let mut atlas = Atlas::new(scaler, render_api);
+    let mut atlas = Atlas::new(scaler, render_api, tag);
     for glyph in glyph_run.glyphs() {
         atlas.push_glyph(glyph.id);
     }

+ 8 - 7
bin/app/src/ui/chatedit.rs

@@ -38,8 +38,8 @@ use std::{
 use crate::{
     error::Result,
     gfx::{
-        GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, GraphicsEventPublisherPtr,
-        Point, Rectangle, RenderApi, Vertex,
+        gfxtag, GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId,
+        GraphicsEventPublisherPtr, Point, Rectangle, RenderApi, Vertex,
     },
     mesh::{Color, MeshBuilder, MeshInfo, COLOR_BLUE, COLOR_RED, COLOR_WHITE},
     prop::{
@@ -467,7 +467,7 @@ impl ChatEdit {
         };
         let cursor_color = self.cursor_color.get();
 
-        let mut mesh = MeshBuilder::new();
+        let mut mesh = MeshBuilder::new(gfxtag!("chatedit_cursor"));
         mesh.draw_filled_box(&cursor_rect, cursor_color);
         mesh.alloc(&self.render_api).draw_untextured()
     }
@@ -1071,7 +1071,7 @@ impl ChatEdit {
 
         let mut rect = self.rect.get().with_zero_pos();
 
-        let mut mesh = MeshBuilder::new();
+        let mut mesh = MeshBuilder::new(gfxtag!("chatedit_bg"));
         mesh.draw_outline(&rect, [0., 1., 0., 1.], 1.);
 
         rect.y = padding_top;
@@ -1087,7 +1087,8 @@ impl ChatEdit {
         let editor = self.lock_editor().await;
         let layout = editor.layout();
 
-        let mut render_instrs = text2::render_layout(layout, &self.render_api);
+        let mut render_instrs =
+            text2::render_layout(layout, &self.render_api, gfxtag!("chatedit_txt_mesh"));
         instrs.append(&mut render_instrs);
 
         instrs
@@ -1104,7 +1105,7 @@ impl ChatEdit {
         let sel = editor.selection();
         let sel_color = self.hi_bg_color.get();
         if !sel.is_collapsed() {
-            let mut mesh = MeshBuilder::new();
+            let mut mesh = MeshBuilder::new(gfxtag!("chatedit_select_mesh"));
             sel.geometry_with(layout, |rect: parley::Rect, _| {
                 mesh.draw_filled_box(&rect.into(), sel_color);
             });
@@ -1133,7 +1134,7 @@ impl ChatEdit {
         let pos = self.inner_pos() + Point::new(0., -scroll);
 
         // We could cache this and use Move instead but why bother?
-        let mut mesh = MeshBuilder::new();
+        let mut mesh = MeshBuilder::new(gfxtag!("chatedit_phone_select_handle"));
         self.draw_phone_select_handle(&mut mesh, first, -1.);
         self.draw_phone_select_handle(&mut mesh, last, 1.);
         vec![

+ 7 - 7
bin/app/src/ui/chatview/page.rs

@@ -31,7 +31,7 @@ use std::{
 use super::{max, MessageId, Timestamp};
 use crate::{
     gfx::{
-        GfxBufferId, GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId,
+        gfxtag, GfxBufferId, GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId,
         GraphicsEventPublisherPtr, ManagedTexturePtr, Point, Rectangle, RenderApi,
     },
     mesh::{Color, MeshBuilder, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_WHITE},
@@ -102,7 +102,7 @@ impl PrivMessage {
         }
         let unwrapped_glyphs = text_shaper.shape(linetext, font_size, window_scale);
 
-        let mut atlas = text::Atlas::new(render_api);
+        let mut atlas = text::Atlas::new(render_api, gfxtag!("chatview_privmsg"));
         atlas.push(&time_glyphs);
         atlas.push(&unwrapped_glyphs);
         let atlas = atlas.make();
@@ -156,7 +156,7 @@ impl PrivMessage {
         }
 
         //t!("gen_mesh({})", glyph_str(&self.unwrapped_glyphs));
-        let mut mesh = MeshBuilder::new();
+        let mut mesh = MeshBuilder::new(gfxtag!("chatview_privmsg"));
 
         if self.is_selected {
             let height = self.height(line_height) + msg_spacing;
@@ -336,7 +336,7 @@ impl PrivMessage {
         };
         self.unwrapped_glyphs = text_shaper.shape(linetext, font_size, window_scale);
 
-        let mut atlas = text::Atlas::new(render_api);
+        let mut atlas = text::Atlas::new(render_api, gfxtag!("chatview_privmsg"));
         atlas.push(&self.time_glyphs);
         atlas.push(&self.unwrapped_glyphs);
         self.atlas = atlas.make();
@@ -401,7 +401,7 @@ impl DateMessage {
 
         let glyphs = text_shaper.shape(datestr, font_size, window_scale);
 
-        let mut atlas = text::Atlas::new(render_api);
+        let mut atlas = text::Atlas::new(render_api, gfxtag!("chatview_datemsg"));
         atlas.push(&glyphs);
         let atlas = atlas.make();
 
@@ -436,7 +436,7 @@ impl DateMessage {
         let datestr = Self::datestr(self.timestamp);
         self.glyphs = text_shaper.shape(datestr, font_size, window_scale);
 
-        let mut atlas = text::Atlas::new(render_api);
+        let mut atlas = text::Atlas::new(render_api, gfxtag!("chatview_datemsg"));
         atlas.push(&self.glyphs);
         self.atlas = atlas.make();
     }
@@ -459,7 +459,7 @@ impl DateMessage {
         debug_render: bool,
         render_api: &RenderApi,
     ) -> GfxDrawMesh {
-        let mut mesh = MeshBuilder::new();
+        let mut mesh = MeshBuilder::new(gfxtag!("chatview_datemsg"));
 
         let glyph_pos_iter =
             GlyphPositionIter::new(self.font_size, self.window_scale, &self.glyphs, baseline);

+ 6 - 5
bin/app/src/ui/editbox/mod.rs

@@ -34,8 +34,8 @@ use std::{
 use crate::{
     error::Result,
     gfx::{
-        GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, GraphicsEventPublisherPtr,
-        Point, Rectangle, RenderApi, Vertex,
+        gfxtag, GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId,
+        GraphicsEventPublisherPtr, Point, Rectangle, RenderApi, Vertex,
     },
     mesh::{MeshBuilder, MeshInfo, COLOR_BLUE, COLOR_WHITE},
     prop::{
@@ -335,9 +335,10 @@ impl EditBox {
         //debug!(target: "ui::editbox", "    cursor_pos={cursor_pos}, is_focused={is_focused}");
 
         let rendered = self.editable.lock().unwrap().render();
-        let atlas = text::make_texture_atlas(&self.render_api, &rendered.glyphs);
+        let atlas =
+            text::make_texture_atlas(&self.render_api, gfxtag!("editbox_txt"), &rendered.glyphs);
 
-        let mut mesh = MeshBuilder::with_clip(clip.clone());
+        let mut mesh = MeshBuilder::with_clip(gfxtag!("editbox_txt"), clip.clone());
 
         let selections = self.select.lock().unwrap().clone();
         // Just an assert
@@ -401,7 +402,7 @@ impl EditBox {
         };
         let cursor_color = self.cursor_color.get();
 
-        let mut mesh = MeshBuilder::new();
+        let mut mesh = MeshBuilder::new(gfxtag!("editbox_cursor"));
         mesh.draw_filled_box(&cursor_rect, cursor_color);
         mesh.alloc(&self.render_api).draw_untextured()
     }

+ 4 - 4
bin/app/src/ui/emoji_picker/emoji.rs

@@ -26,8 +26,8 @@ use std::{
 
 use crate::{
     gfx::{
-        GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, ManagedTexturePtr, Point,
-        Rectangle, RenderApi,
+        gfxtag, GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, ManagedTexturePtr,
+        Point, Rectangle, RenderApi,
     },
     mesh::{MeshBuilder, MeshInfo, COLOR_WHITE},
     prop::{
@@ -104,7 +104,7 @@ impl EmojiMeshes {
         // The params here don't actually matter since we're talking about BMP fixed sizes
         let glyphs = self.text_shaper.shape(emoji.to_string(), 10., 1.);
         assert_eq!(glyphs.len(), 1);
-        let atlas = text::make_texture_atlas(&self.render_api, &glyphs);
+        let atlas = text::make_texture_atlas(&self.render_api, gfxtag!("emoji_mesh"), &glyphs);
         let glyph = glyphs.into_iter().next().unwrap();
 
         // Emoji's vary in size. We make them all a consistent size.
@@ -116,7 +116,7 @@ impl EmojiMeshes {
         let y = -h / 2.;
 
         let uv = atlas.fetch_uv(glyph.glyph_id).expect("missing glyph UV rect");
-        let mut mesh = MeshBuilder::new();
+        let mut mesh = MeshBuilder::new(gfxtag!("emoji_mesh"));
         mesh.draw_box(&Rectangle::new(x, y, w, h), COLOR_WHITE, &uv);
         mesh.alloc(&self.render_api).draw_with_texture(atlas.texture)
     }

+ 4 - 4
bin/app/src/ui/image.rs

@@ -27,8 +27,8 @@ use std::{
 
 use crate::{
     gfx::{
-        GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, ManagedTexturePtr, Rectangle,
-        RenderApi,
+        gfxtag, GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, ManagedTexturePtr,
+        Rectangle, RenderApi,
     },
     mesh::{MeshBuilder, MeshInfo, COLOR_WHITE},
     prop::{PropertyAtomicGuard, PropertyPtr, PropertyRect, PropertyStr, PropertyUint32, Role},
@@ -126,7 +126,7 @@ impl Image {
         let height = img.height() as u16;
         let bmp = img.into_raw();
 
-        self.render_api.new_texture(width, height, bmp)
+        self.render_api.new_texture(width, height, bmp, gfxtag!("img"))
     }
 
     async fn redraw(self: Arc<Self>) {
@@ -148,7 +148,7 @@ impl Image {
         let rect = self.rect.get();
         let uv = self.uv.get();
         let mesh_rect = Rectangle::from([0., 0., rect.w, rect.h]);
-        let mut mesh = MeshBuilder::new();
+        let mut mesh = MeshBuilder::new(gfxtag!("img"));
         mesh.draw_box(&mesh_rect, COLOR_WHITE, &uv);
         mesh.alloc(&self.render_api)
     }

+ 3 - 3
bin/app/src/ui/text.rs

@@ -23,8 +23,8 @@ use std::sync::{Arc, Weak};
 
 use crate::{
     gfx::{
-        GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, ManagedTexturePtr, Point,
-        Rectangle, RenderApi,
+        gfxtag, GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, ManagedTexturePtr,
+        Point, Rectangle, RenderApi,
     },
     mesh::{Color, MeshBuilder, MeshInfo, COLOR_BLUE, COLOR_RED, COLOR_WHITE},
     prop::{
@@ -138,7 +138,7 @@ impl Text {
             debug_opts |= text2::DebugRenderOptions::Baseline;
         }
 
-        text2::render_layout_with_opts(&layout, debug_opts, &self.render_api)
+        text2::render_layout_with_opts(&layout, debug_opts, &self.render_api, gfxtag!("text"))
     }
 
     async fn redraw(self: Arc<Self>) {

+ 5 - 3
bin/app/src/ui/vector_art/mod.rs

@@ -25,7 +25,8 @@ use crate::{
     error::{Error, Result},
     expr::{Op, SExprCode, SExprMachine, SExprVal},
     gfx::{
-        GfxBufferId, GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, Rectangle, RenderApi, Vertex,
+        gfxtag, GfxBufferId, GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, Rectangle, RenderApi,
+        Vertex,
     },
     mesh::Color,
     prop::{
@@ -127,8 +128,9 @@ impl VectorArt {
         let verts = self.shape.eval(rect.w, rect.h).expect("bad shape");
 
         //debug!(target: "ui::vector_art", "=> {verts:#?}");
-        let vertex_buffer = self.render_api.new_vertex_buffer(verts);
-        let index_buffer = self.render_api.new_index_buffer(self.shape.indices.clone());
+        let vertex_buffer = self.render_api.new_vertex_buffer(verts, gfxtag!("vectorart"));
+        let index_buffer =
+            self.render_api.new_index_buffer(self.shape.indices.clone(), gfxtag!("vectorart"));
         let mesh = GfxDrawMesh {
             vertex_buffer,
             index_buffer,