فهرست منبع

wallet: make textures managed and auto-delete when there's no longer any refs to them

darkfi 1 سال پیش
والد
کامیت
0bd20aefaf

+ 37 - 17
bin/darkwallet/src/gfx/mod.rs

@@ -34,7 +34,6 @@ use std::{
 
 
 mod linalg;
 mod linalg;
 pub use linalg::{Dimension, Point, Rectangle};
 pub use linalg::{Dimension, Point, Rectangle};
-mod scr;
 mod shader;
 mod shader;
 
 
 use crate::{
 use crate::{
@@ -69,6 +68,27 @@ impl Vertex {
     }
     }
 }
 }
 
 
+pub type ManagedTexturePtr = Arc<ManagedTexture>;
+
+/// Auto-deletes texture on drop
+#[derive(Clone)]
+pub struct ManagedTexture {
+    id: GfxTextureId,
+    render_api: RenderApi,
+}
+
+impl Drop for ManagedTexture {
+    fn drop(&mut self) {
+        self.render_api.delete_texture(self.id);
+    }
+}
+
+impl std::fmt::Debug for ManagedTexture {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("ManagedTexture").field("id", &self.id).finish()
+    }
+}
+
 #[derive(Clone)]
 #[derive(Clone)]
 pub struct RenderApi {
 pub struct RenderApi {
     method_req: mpsc::Sender<GraphicsMethod>,
     method_req: mpsc::Sender<GraphicsMethod>,
@@ -79,7 +99,7 @@ impl RenderApi {
         Self { method_req }
         Self { method_req }
     }
     }
 
 
-    pub fn new_texture(&self, width: u16, height: u16, data: Vec<u8>) -> GfxTextureId {
+    pub fn new_unmanaged_texture(&self, width: u16, height: u16, data: Vec<u8>) -> GfxTextureId {
         let gfx_texture_id = rand::random();
         let gfx_texture_id = rand::random();
 
 
         let method = GraphicsMethod::NewTexture((width, height, data, gfx_texture_id));
         let method = GraphicsMethod::NewTexture((width, height, data, gfx_texture_id));
@@ -88,6 +108,13 @@ impl RenderApi {
         gfx_texture_id
         gfx_texture_id
     }
     }
 
 
+    pub fn new_texture(&self, width: u16, height: u16, data: Vec<u8>) -> ManagedTexturePtr {
+        Arc::new(ManagedTexture {
+            id: self.new_unmanaged_texture(width, height, data),
+            render_api: self.clone(),
+        })
+    }
+
     pub fn delete_texture(&self, texture: GfxTextureId) {
     pub fn delete_texture(&self, texture: GfxTextureId) {
         let method = GraphicsMethod::DeleteTexture(texture);
         let method = GraphicsMethod::DeleteTexture(texture);
         let _ = self.method_req.send(method);
         let _ = self.method_req.send(method);
@@ -122,11 +149,11 @@ impl RenderApi {
     }
     }
 }
 }
 
 
-#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
+#[derive(Clone, Debug)]
 pub struct GfxDrawMesh {
 pub struct GfxDrawMesh {
     pub vertex_buffer: GfxBufferId,
     pub vertex_buffer: GfxBufferId,
     pub index_buffer: GfxBufferId,
     pub index_buffer: GfxBufferId,
-    pub texture: Option<GfxTextureId>,
+    pub texture: Option<ManagedTexturePtr>,
     pub num_elements: i32,
     pub num_elements: i32,
 }
 }
 
 
@@ -139,13 +166,13 @@ impl GfxDrawMesh {
         DrawMesh {
         DrawMesh {
             vertex_buffer: buffers[&self.vertex_buffer],
             vertex_buffer: buffers[&self.vertex_buffer],
             index_buffer: buffers[&self.index_buffer],
             index_buffer: buffers[&self.index_buffer],
-            texture: self.texture.map(|t| textures[&t]),
+            texture: self.texture.map(|t| (t.clone(), textures[&t.id])),
             num_elements: self.num_elements,
             num_elements: self.num_elements,
         }
         }
     }
     }
 }
 }
 
 
-#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone)]
 pub enum GfxDrawInstruction {
 pub enum GfxDrawInstruction {
     SetScale(f32),
     SetScale(f32),
     Move(Point),
     Move(Point),
@@ -168,7 +195,7 @@ impl GfxDrawInstruction {
     }
     }
 }
 }
 
 
-#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
+#[derive(Clone, Debug)]
 pub struct GfxDrawCall {
 pub struct GfxDrawCall {
     pub instrs: Vec<GfxDrawInstruction>,
     pub instrs: Vec<GfxDrawInstruction>,
     pub dcs: Vec<u64>,
     pub dcs: Vec<u64>,
@@ -193,7 +220,7 @@ impl GfxDrawCall {
 struct DrawMesh {
 struct DrawMesh {
     vertex_buffer: miniquad::BufferId,
     vertex_buffer: miniquad::BufferId,
     index_buffer: miniquad::BufferId,
     index_buffer: miniquad::BufferId,
-    texture: Option<miniquad::TextureId>,
+    texture: Option<(ManagedTexturePtr, miniquad::TextureId)>,
     num_elements: i32,
     num_elements: i32,
 }
 }
 
 
@@ -312,7 +339,7 @@ impl<'a> RenderContext<'a> {
                         debug!(target: "gfx", "{ws}draw({mesh:?})");
                         debug!(target: "gfx", "{ws}draw({mesh:?})");
                     }
                     }
                     let texture = match mesh.texture {
                     let texture = match mesh.texture {
-                        Some(texture) => texture,
+                        Some((_, texture)) => texture,
                         None => self.white_texture,
                         None => self.white_texture,
                     };
                     };
                     let bindings = Bindings {
                     let bindings = Bindings {
@@ -345,7 +372,7 @@ impl<'a> RenderContext<'a> {
     }
     }
 }
 }
 
 
-#[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
+#[derive(Clone, Debug)]
 pub enum GraphicsMethod {
 pub enum GraphicsMethod {
     NewTexture((u16, u16, Vec<u8>, GfxTextureId)),
     NewTexture((u16, u16, Vec<u8>, GfxTextureId)),
     DeleteTexture(GfxTextureId),
     DeleteTexture(GfxTextureId),
@@ -464,8 +491,6 @@ struct Stage {
 
 
     method_rep: mpsc::Receiver<GraphicsMethod>,
     method_rep: mpsc::Receiver<GraphicsMethod>,
     event_pub: GraphicsEventPublisherPtr,
     event_pub: GraphicsEventPublisherPtr,
-
-    draw_log: Option<scr::DrawLog>,
 }
 }
 
 
 impl Stage {
 impl Stage {
@@ -532,16 +557,11 @@ impl Stage {
             buffers: HashMap::new(),
             buffers: HashMap::new(),
             method_rep,
             method_rep,
             event_pub,
             event_pub,
-            draw_log: if DEBUG_DRAW_LOG { Some(scr::DrawLog::new()) } else { None },
         }
         }
     }
     }
 
 
     fn process_method(&mut self, method: GraphicsMethod) {
     fn process_method(&mut self, method: GraphicsMethod) {
         //debug!(target: "gfx", "Received method: {:?}", method);
         //debug!(target: "gfx", "Received method: {:?}", method);
-        if let Some(dlog) = &mut self.draw_log {
-            dlog.log(method.clone());
-        }
-
         match method {
         match method {
             GraphicsMethod::NewTexture((width, height, data, gfx_texture_id)) => {
             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)

+ 0 - 66
bin/darkwallet/src/gfx/scr.rs

@@ -1,66 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2024 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use async_trait::async_trait;
-use darkfi_serial::{
-    deserialize, serialize, Decodable, Encodable, SerialDecodable, SerialEncodable,
-};
-use std::{
-    fs::{File, OpenOptions},
-    time::Instant,
-};
-
-use super::GraphicsMethod;
-
-const FILENAME: &str = "drawinstrs.dat";
-
-#[derive(Debug, SerialEncodable, SerialDecodable)]
-struct Instruction {
-    timest: u64,
-    method: GraphicsMethod,
-}
-
-pub struct DrawLog {
-    instant: Instant,
-    fd: File,
-}
-
-impl DrawLog {
-    pub fn new() -> Self {
-        let instant = Instant::now();
-        let fd = OpenOptions::new().write(true).create(true).open(FILENAME).unwrap();
-        Self { instant, fd }
-    }
-
-    pub fn log(&mut self, method: GraphicsMethod) {
-        let instr = Instruction { timest: self.instant.elapsed().as_millis() as u64, method };
-        let data = serialize(&instr);
-        data.encode(&mut self.fd).unwrap();
-    }
-
-    pub fn read() -> Vec<Instruction> {
-        let mut instrs = vec![];
-        let mut f = File::open(FILENAME).unwrap();
-        loop {
-            let Ok(data) = Vec::<u8>::decode(&mut f) else { break };
-
-            let instr: Instruction = deserialize(&data).unwrap();
-        }
-        instrs
-    }
-}

+ 4 - 2
bin/darkwallet/src/mesh.rs

@@ -18,7 +18,9 @@
 
 
 use crate::{
 use crate::{
     error::Result,
     error::Result,
-    gfx::{GfxBufferId, GfxDrawMesh, GfxTextureId, Rectangle, RenderApi, Vertex},
+    gfx::{
+        GfxBufferId, GfxDrawMesh, GfxTextureId, ManagedTexturePtr, Rectangle, RenderApi, Vertex,
+    },
 };
 };
 
 
 pub type Color = [f32; 4];
 pub type Color = [f32; 4];
@@ -47,7 +49,7 @@ pub struct MeshInfo {
 
 
 impl MeshInfo {
 impl MeshInfo {
     /// Convenience method
     /// Convenience method
-    pub fn draw_with_texture(self, texture: GfxTextureId) -> GfxDrawMesh {
+    pub fn draw_with_texture(self, texture: ManagedTexturePtr) -> GfxDrawMesh {
         GfxDrawMesh {
         GfxDrawMesh {
             vertex_buffer: self.vertex_buffer,
             vertex_buffer: self.vertex_buffer,
             index_buffer: self.index_buffer,
             index_buffer: self.index_buffer,

+ 5 - 5
bin/darkwallet/src/text/atlas.rs

@@ -1,7 +1,7 @@
 use super::{Glyph, Sprite, SpritePtr};
 use super::{Glyph, Sprite, SpritePtr};
 use crate::{
 use crate::{
     error::Result,
     error::Result,
-    gfx::{GfxTextureId, Rectangle, RenderApi},
+    gfx::{GfxTextureId, ManagedTexturePtr, Rectangle, RenderApi},
 };
 };
 
 
 /// Prevents render artifacts from aliasing.
 /// Prevents render artifacts from aliasing.
@@ -138,12 +138,12 @@ impl<'a> Atlas<'a> {
         assert_eq!(self.glyph_ids.len(), self.x_pos.len());
         assert_eq!(self.glyph_ids.len(), self.x_pos.len());
 
 
         let atlas = self.render();
         let atlas = self.render();
-        let texture_id = 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);
 
 
         let uv_rects = self.compute_uvs();
         let uv_rects = self.compute_uvs();
         let glyph_ids = self.glyph_ids;
         let glyph_ids = self.glyph_ids;
 
 
-        RenderedAtlas { glyph_ids, uv_rects, texture_id }
+        RenderedAtlas { glyph_ids, uv_rects, texture }
     }
     }
 }
 }
 
 
@@ -172,8 +172,8 @@ pub struct RenderedAtlas {
     glyph_ids: Vec<u32>,
     glyph_ids: Vec<u32>,
     /// UV rectangle within the texture.
     /// UV rectangle within the texture.
     uv_rects: Vec<Rectangle>,
     uv_rects: Vec<Rectangle>,
-    /// Allocated atlas texture. Must be manually deallocated by the user.
-    pub texture_id: GfxTextureId,
+    /// Allocated atlas texture.
+    pub texture: ManagedTexturePtr,
 }
 }
 
 
 impl RenderedAtlas {
 impl RenderedAtlas {

+ 0 - 3
bin/darkwallet/src/ui/chatview/mod.rs

@@ -679,9 +679,6 @@ impl ChatView {
         for buffer_id in freed.buffers {
         for buffer_id in freed.buffers {
             self.render_api.delete_buffer(buffer_id);
             self.render_api.delete_buffer(buffer_id);
         }
         }
-        for texture_id in freed.textures {
-            self.render_api.delete_texture(texture_id);
-        }
     }
     }
 
 
     /// Invalidates cache and redraws everything
     /// Invalidates cache and redraws everything

+ 7 - 17
bin/darkwallet/src/ui/chatview/page.rs

@@ -32,7 +32,7 @@ use super::{max, MessageId, Timestamp};
 use crate::{
 use crate::{
     gfx::{
     gfx::{
         GfxBufferId, GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId,
         GfxBufferId, GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId,
-        GraphicsEventPublisherPtr, Point, Rectangle, RenderApi,
+        GraphicsEventPublisherPtr, ManagedTexturePtr, Point, Rectangle, RenderApi,
     },
     },
     mesh::{Color, MeshBuilder, COLOR_BLUE, COLOR_GREEN, COLOR_PINK},
     mesh::{Color, MeshBuilder, COLOR_BLUE, COLOR_GREEN, COLOR_PINK},
     prop::{PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyUint32, Role},
     prop::{PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyUint32, Role},
@@ -201,7 +201,7 @@ impl PrivMessage {
         }
         }
 
 
         let mesh = mesh.alloc(render_api);
         let mesh = mesh.alloc(render_api);
-        let mesh = mesh.draw_with_texture(self.atlas.texture_id);
+        let mesh = mesh.draw_with_texture(self.atlas.texture.clone());
         self.mesh_cache = Some(mesh.clone());
         self.mesh_cache = Some(mesh.clone());
 
 
         mesh
         mesh
@@ -292,7 +292,7 @@ impl PrivMessage {
         timestamp_width: f32,
         timestamp_width: f32,
         text_shaper: &TextShaper,
         text_shaper: &TextShaper,
         render_api: &RenderApi,
         render_api: &RenderApi,
-    ) -> GfxTextureId {
+    ) {
         self.font_size = font_size;
         self.font_size = font_size;
         self.timestamp_font_size = timestamp_font_size;
         self.timestamp_font_size = timestamp_font_size;
         self.window_scale = window_scale;
         self.window_scale = window_scale;
@@ -303,8 +303,6 @@ impl PrivMessage {
         let linetext = format!("{} {}", self.nick, self.text);
         let linetext = format!("{} {}", self.nick, self.text);
         self.unwrapped_glyphs = text_shaper.shape(linetext, font_size, window_scale);
         self.unwrapped_glyphs = text_shaper.shape(linetext, font_size, window_scale);
 
 
-        let texture_id = self.atlas.texture_id;
-
         let mut atlas = text::Atlas::new(render_api);
         let mut atlas = text::Atlas::new(render_api);
         atlas.push(&self.time_glyphs);
         atlas.push(&self.time_glyphs);
         atlas.push(&self.unwrapped_glyphs);
         atlas.push(&self.unwrapped_glyphs);
@@ -312,8 +310,6 @@ impl PrivMessage {
 
 
         // We need to rewrap the glyphs since they've been reloaded
         // We need to rewrap the glyphs since they've been reloaded
         self.adjust_width(line_width, timestamp_width);
         self.adjust_width(line_width, timestamp_width);
-
-        texture_id
     }
     }
 
 
     /// clear_mesh() must be called after this.
     /// clear_mesh() must be called after this.
@@ -399,20 +395,16 @@ impl DateMessage {
         window_scale: f32,
         window_scale: f32,
         text_shaper: &TextShaper,
         text_shaper: &TextShaper,
         render_api: &RenderApi,
         render_api: &RenderApi,
-    ) -> GfxTextureId {
+    ) {
         self.font_size = font_size;
         self.font_size = font_size;
         self.window_scale = window_scale;
         self.window_scale = window_scale;
 
 
         let datestr = Self::datestr(self.timestamp);
         let datestr = Self::datestr(self.timestamp);
         self.glyphs = text_shaper.shape(datestr, font_size, window_scale);
         self.glyphs = text_shaper.shape(datestr, font_size, window_scale);
 
 
-        let texture_id = self.atlas.texture_id;
-
         let mut atlas = text::Atlas::new(render_api);
         let mut atlas = text::Atlas::new(render_api);
         atlas.push(&self.glyphs);
         atlas.push(&self.glyphs);
         self.atlas = atlas.make();
         self.atlas = atlas.make();
-
-        texture_id
     }
     }
 
 
     //fn adjust_width(&mut self, line_width: f32) { }
     //fn adjust_width(&mut self, line_width: f32) { }
@@ -451,7 +443,7 @@ impl DateMessage {
         }
         }
 
 
         let mesh = mesh.alloc(render_api);
         let mesh = mesh.alloc(render_api);
-        let mesh = mesh.draw_with_texture(self.atlas.texture_id);
+        let mesh = mesh.draw_with_texture(self.atlas.texture.clone());
         self.mesh_cache = Some(mesh.clone());
         self.mesh_cache = Some(mesh.clone());
 
 
         mesh
         mesh
@@ -497,7 +489,7 @@ impl Message {
         timestamp_width: f32,
         timestamp_width: f32,
         text_shaper: &TextShaper,
         text_shaper: &TextShaper,
         render_api: &RenderApi,
         render_api: &RenderApi,
-    ) -> GfxTextureId {
+    ) {
         match self {
         match self {
             Self::Priv(m) => m.adjust_params(
             Self::Priv(m) => m.adjust_params(
                 font_size,
                 font_size,
@@ -705,7 +697,7 @@ impl MessageBuffer {
         debug!(target: "ui::chatview::page", "{:?}: freeing old textures", self.node());
         debug!(target: "ui::chatview::page", "{:?}: freeing old textures", self.node());
 
 
         for msg in &mut self.msgs {
         for msg in &mut self.msgs {
-            let old_texture_id = msg.adjust_params(
+            msg.adjust_params(
                 font_size,
                 font_size,
                 timestamp_font_size,
                 timestamp_font_size,
                 window_scale,
                 window_scale,
@@ -714,8 +706,6 @@ impl MessageBuffer {
                 &self.text_shaper,
                 &self.text_shaper,
                 &self.render_api,
                 &self.render_api,
             );
             );
-
-            self.freed.add_texture(old_texture_id);
         }
         }
     }
     }
 
 

+ 2 - 16
bin/darkwallet/src/ui/editbox.rs

@@ -462,7 +462,7 @@ impl EditBox {
             mesh.draw_outline(&clip, COLOR_BLUE, 1.);
             mesh.draw_outline(&clip, COLOR_BLUE, 1.);
         }
         }
 
 
-        mesh.alloc(&self.render_api).draw_with_texture(atlas.texture_id)
+        mesh.alloc(&self.render_api).draw_with_texture(atlas.texture)
     }
     }
 
 
     fn regen_cursor_mesh(&self) -> GfxDrawMesh {
     fn regen_cursor_mesh(&self) -> GfxDrawMesh {
@@ -1187,9 +1187,6 @@ impl EditBox {
         for buffer_id in draw_update.freed_buffers {
         for buffer_id in draw_update.freed_buffers {
             self.render_api.delete_buffer(buffer_id);
             self.render_api.delete_buffer(buffer_id);
         }
         }
-        for texture_id in draw_update.freed_textures {
-            self.render_api.delete_texture(texture_id);
-        }
     }
     }
 
 
     async fn redraw_cursor(&self) {
     async fn redraw_cursor(&self) {
@@ -1240,10 +1237,6 @@ impl EditBox {
             let text_mesh = std::mem::replace(&mut *self.text_mesh.lock().unwrap(), None);
             let text_mesh = std::mem::replace(&mut *self.text_mesh.lock().unwrap(), None);
             // We're finished with these so clean up.
             // We're finished with these so clean up.
             if let Some(old) = text_mesh {
             if let Some(old) = text_mesh {
-                if let Some(texture) = old.texture {
-                    //debug!(target: "ui::editbox", "{:?}: freeing old texture", self.node());
-                    freed.textures.push(texture);
-                }
                 freed.buffers.push(old.vertex_buffer);
                 freed.buffers.push(old.vertex_buffer);
                 freed.buffers.push(old.index_buffer);
                 freed.buffers.push(old.index_buffer);
             }
             }
@@ -1255,10 +1248,6 @@ impl EditBox {
 
 
         // We're finished with these so clean up.
         // We're finished with these so clean up.
         if let Some(old) = old_text_mesh {
         if let Some(old) = old_text_mesh {
-            if let Some(texture) = old.texture {
-                //debug!(target: "ui::editbox", "{:?}: freeing old texture", self.node());
-                freed.textures.push(texture);
-            }
             freed.buffers.push(old.vertex_buffer);
             freed.buffers.push(old.vertex_buffer);
             freed.buffers.push(old.index_buffer);
             freed.buffers.push(old.index_buffer);
         }
         }
@@ -1284,7 +1273,7 @@ impl EditBox {
                     GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: self.z_index.get() },
                     GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: self.z_index.get() },
                 ),
                 ),
             ],
             ],
-            freed_textures: freed.textures,
+            freed_textures: vec![],
             freed_buffers: freed.buffers,
             freed_buffers: freed.buffers,
         })
         })
     }
     }
@@ -1295,9 +1284,6 @@ impl Drop for EditBox {
         let text_mesh = std::mem::replace(&mut *self.text_mesh.lock().unwrap(), None);
         let text_mesh = std::mem::replace(&mut *self.text_mesh.lock().unwrap(), None);
         // We're finished with these so clean up.
         // We're finished with these so clean up.
         if let Some(old) = text_mesh {
         if let Some(old) = text_mesh {
-            if let Some(texture) = old.texture {
-                self.render_api.delete_texture(texture);
-            }
             self.render_api.delete_buffer(old.vertex_buffer);
             self.render_api.delete_buffer(old.vertex_buffer);
             self.render_api.delete_buffer(old.index_buffer);
             self.render_api.delete_buffer(old.index_buffer);
         }
         }

+ 10 - 13
bin/darkwallet/src/ui/image.rs

@@ -25,7 +25,10 @@ use std::{
 };
 };
 
 
 use crate::{
 use crate::{
-    gfx::{GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, Rectangle, RenderApi},
+    gfx::{
+        GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, ManagedTexturePtr, Rectangle,
+        RenderApi,
+    },
     mesh::{MeshBuilder, MeshInfo, COLOR_WHITE},
     mesh::{MeshBuilder, MeshInfo, COLOR_WHITE},
     prop::{PropertyPtr, PropertyRect, PropertyStr, PropertyUint32, Role},
     prop::{PropertyPtr, PropertyRect, PropertyStr, PropertyUint32, Role},
     scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
     scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
@@ -42,7 +45,7 @@ pub struct Image {
     tasks: OnceLock<Vec<smol::Task<()>>>,
     tasks: OnceLock<Vec<smol::Task<()>>>,
 
 
     mesh: SyncMutex<Option<MeshInfo>>,
     mesh: SyncMutex<Option<MeshInfo>>,
-    texture: SyncMutex<Option<GfxTextureId>>,
+    texture: SyncMutex<Option<ManagedTexturePtr>>,
     dc_key: u64,
     dc_key: u64,
 
 
     rect: PropertyRect,
     rect: PropertyRect,
@@ -93,13 +96,9 @@ impl Image {
         let old_texture = std::mem::replace(&mut *self.texture.lock().unwrap(), Some(texture));
         let old_texture = std::mem::replace(&mut *self.texture.lock().unwrap(), Some(texture));
 
 
         self.clone().redraw().await;
         self.clone().redraw().await;
-
-        if let Some(old_texture) = old_texture {
-            self.render_api.delete_texture(old_texture);
-        }
     }
     }
 
 
-    fn load_texture(&self) -> GfxTextureId {
+    fn load_texture(&self) -> ManagedTexturePtr {
         let path = self.path.get();
         let path = self.path.get();
 
 
         // TODO we should NOT use panic here
         // TODO we should NOT use panic here
@@ -123,8 +122,8 @@ impl Image {
         let height = img.height() as u16;
         let height = img.height() as u16;
         let bmp = img.into_raw();
         let bmp = img.into_raw();
 
 
-        let texture_id = self.render_api.new_texture(width, height, bmp);
-        texture_id
+        let texture = self.render_api.new_texture(width, height, bmp);
+        texture
     }
     }
 
 
     async fn redraw(self: Arc<Self>) {
     async fn redraw(self: Arc<Self>) {
@@ -160,7 +159,7 @@ impl Image {
         let mesh = self.regen_mesh();
         let mesh = self.regen_mesh();
         let old_mesh = std::mem::replace(&mut *self.mesh.lock().unwrap(), Some(mesh.clone()));
         let old_mesh = std::mem::replace(&mut *self.mesh.lock().unwrap(), Some(mesh.clone()));
 
 
-        let texture_id = self.texture.lock().unwrap().expect("Node missing texture_id!");
+        let texture = self.texture.lock().unwrap().clone().expect("Node missing texture_id!");
 
 
         // We're finished with these so clean up.
         // We're finished with these so clean up.
         let mut freed_buffers = vec![];
         let mut freed_buffers = vec![];
@@ -172,7 +171,7 @@ impl Image {
         let mesh = GfxDrawMesh {
         let mesh = GfxDrawMesh {
             vertex_buffer: mesh.vertex_buffer,
             vertex_buffer: mesh.vertex_buffer,
             index_buffer: mesh.index_buffer,
             index_buffer: mesh.index_buffer,
-            texture: Some(texture_id),
+            texture: Some(texture),
             num_elements: mesh.num_elements,
             num_elements: mesh.num_elements,
         };
         };
 
 
@@ -236,7 +235,5 @@ impl Drop for Image {
             self.render_api.delete_buffer(vertex_buffer);
             self.render_api.delete_buffer(vertex_buffer);
             self.render_api.delete_buffer(index_buffer);
             self.render_api.delete_buffer(index_buffer);
         }
         }
-        let texture_id = self.texture.lock().unwrap().unwrap();
-        self.render_api.delete_texture(texture_id);
     }
     }
 }
 }

+ 8 - 8
bin/darkwallet/src/ui/text.rs

@@ -21,7 +21,10 @@ use rand::{rngs::OsRng, Rng};
 use std::sync::{Arc, Mutex as SyncMutex, OnceLock, Weak};
 use std::sync::{Arc, Mutex as SyncMutex, OnceLock, Weak};
 
 
 use crate::{
 use crate::{
-    gfx::{GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, Rectangle, RenderApi},
+    gfx::{
+        GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, ManagedTexturePtr, Rectangle,
+        RenderApi,
+    },
     mesh::{Color, MeshBuilder, MeshInfo, COLOR_BLUE, COLOR_WHITE},
     mesh::{Color, MeshBuilder, MeshInfo, COLOR_BLUE, COLOR_WHITE},
     prop::{
     prop::{
         PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyRect, PropertyStr,
         PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyRect, PropertyStr,
@@ -39,7 +42,7 @@ pub type TextPtr = Arc<Text>;
 #[derive(Clone)]
 #[derive(Clone)]
 struct TextRenderInfo {
 struct TextRenderInfo {
     mesh: MeshInfo,
     mesh: MeshInfo,
-    texture_id: GfxTextureId,
+    texture: ManagedTexturePtr,
 }
 }
 
 
 pub struct Text {
 pub struct Text {
@@ -151,7 +154,7 @@ impl Text {
 
 
         let mesh = mesh.alloc(&render_api);
         let mesh = mesh.alloc(&render_api);
 
 
-        TextRenderInfo { mesh, texture_id: atlas.texture_id }
+        TextRenderInfo { mesh, texture: atlas.texture }
     }
     }
 
 
     async fn redraw(self: Arc<Self>) {
     async fn redraw(self: Arc<Self>) {
@@ -165,9 +168,6 @@ impl Text {
         debug!(target: "ui::text", "replace draw calls done");
         debug!(target: "ui::text", "replace draw calls done");
 
 
         // We're finished with these so clean up.
         // We're finished with these so clean up.
-        for texture in draw_update.freed_textures {
-            self.render_api.delete_texture(texture);
-        }
         for buff in draw_update.freed_buffers {
         for buff in draw_update.freed_buffers {
             self.render_api.delete_buffer(buff);
             self.render_api.delete_buffer(buff);
         }
         }
@@ -196,7 +196,7 @@ impl Text {
         let mesh = GfxDrawMesh {
         let mesh = GfxDrawMesh {
             vertex_buffer: render_info.mesh.vertex_buffer,
             vertex_buffer: render_info.mesh.vertex_buffer,
             index_buffer: render_info.mesh.index_buffer,
             index_buffer: render_info.mesh.index_buffer,
-            texture: Some(render_info.texture_id),
+            texture: Some(render_info.texture),
             num_elements: render_info.mesh.num_elements,
             num_elements: render_info.mesh.num_elements,
         };
         };
 
 
@@ -213,7 +213,7 @@ impl Text {
                     z_index: self.z_index.get(),
                     z_index: self.z_index.get(),
                 },
                 },
             )],
             )],
-            freed_textures: vec![old_render_info.texture_id],
+            freed_textures: vec![],
             freed_buffers: vec![
             freed_buffers: vec![
                 old_render_info.mesh.vertex_buffer,
                 old_render_info.mesh.vertex_buffer,
                 old_render_info.mesh.index_buffer,
                 old_render_info.mesh.index_buffer,