Просмотр исходного кода

wallet/gfx: draw calls now have a timestamp associated with them. When updating a draw call, if the update's timestamp is older than the last update then just quietly drop it.

darkfi 1 год назад
Родитель
Сommit
8861a3d78c

+ 7 - 4
bin/darkwallet/gui/print_tree.py

@@ -6,11 +6,14 @@ def join(parent_path, child_name):
         return f"/{child_name}"
     return f"{parent_path}/{child_name}"
 
-def print_tree(node_path="/"):
+def print_tree(node_path="/", depth=None):
     print(node_path)
-    print_node_info(node_path, indent=1)
+    print_node_info(node_path, depth, indent=1)
+
+def print_node_info(parent_path, depth, indent):
+    if indent - 1 == depth:
+        return
 
-def print_node_info(parent_path, indent):
     ws = " "*4*indent
     for (child_name, child_id, child_type) in api.get_children(parent_path):
         match child_type:
@@ -57,7 +60,7 @@ def print_node_info(parent_path, indent):
         else:
             child_path = parent_path + "/" + child_name
 
-        print_node_info(child_path, indent+1)
+        print_node_info(child_path, depth, indent+1)
 
     for prop in api.get_properties(parent_path):
         if prop.type != PropertyType.BUFFER:

+ 7 - 1
bin/darkwallet/src/gfx/linalg.rs

@@ -130,7 +130,7 @@ impl SubAssign for Point {
     }
 }
 
-#[derive(Debug, Clone, Copy, SerialEncodable, SerialDecodable)]
+#[derive(Clone, Copy, SerialEncodable, SerialDecodable)]
 pub struct Rectangle {
     pub x: f32,
     pub y: f32,
@@ -277,3 +277,9 @@ impl Div<f32> for Rectangle {
         Self { x: self.x / scale, y: self.y / scale, w: self.w / scale, h: self.h / scale }
     }
 }
+
+impl std::fmt::Debug for Rectangle {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "({}, {}, {}, {})", self.x, self.y, self.w, self.h)
+    }
+}

+ 31 - 12
bin/darkwallet/src/gfx/mod.rs

@@ -49,7 +49,6 @@ pub type GfxBufferId = u32;
 // This is very noisy so suppress output by default
 const DEBUG_RENDER: bool = false;
 const DEBUG_GFXAPI: bool = false;
-const DEBUG_DRAW_LOG: bool = false;
 
 #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
 #[repr(C)]
@@ -177,8 +176,8 @@ impl RenderApi {
         let _ = self.method_req.send(method);
     }
 
-    pub fn replace_draw_calls(&self, dcs: Vec<(u64, GfxDrawCall)>) {
-        let method = GraphicsMethod::ReplaceDrawCalls(dcs);
+    pub fn replace_draw_calls(&self, timest: u64, dcs: Vec<(u64, GfxDrawCall)>) {
+        let method = GraphicsMethod::ReplaceDrawCalls { timest, dcs };
         let _ = self.method_req.send(method);
     }
 }
@@ -243,11 +242,13 @@ impl GfxDrawCall {
         self,
         textures: &HashMap<GfxTextureId, miniquad::TextureId>,
         buffers: &HashMap<GfxBufferId, miniquad::BufferId>,
+        timest: u64,
     ) -> DrawCall {
         DrawCall {
             instrs: self.instrs.into_iter().map(|i| i.compile(textures, buffers)).collect(),
             dcs: self.dcs,
             z_index: self.z_index,
+            timest,
         }
     }
 }
@@ -275,6 +276,7 @@ struct DrawCall {
     instrs: Vec<DrawInstruction>,
     dcs: Vec<u64>,
     z_index: u32,
+    timest: u64,
 }
 
 struct RenderContext<'a> {
@@ -316,9 +318,9 @@ impl<'a> RenderContext<'a> {
             return
         }
 
-        //if DEBUG_RENDER {
-        //    debug!(target: "gfx", "=> viewport {view_x} {view_y} {view_w} {view_h}");
-        //}
+        if DEBUG_RENDER {
+            debug!(target: "gfx", "=> viewport {view_x} {view_y} {view_w} {view_h}");
+        }
         self.ctx.apply_viewport(view_x, view_y, view_w, view_h);
         self.ctx.apply_scissor_rect(view_x, view_y, view_w, view_h);
     }
@@ -417,7 +419,7 @@ pub enum GraphicsMethod {
     NewVertexBuffer((Vec<Vertex>, GfxBufferId)),
     NewIndexBuffer((Vec<u16>, GfxBufferId)),
     DeleteBuffer(GfxBufferId),
-    ReplaceDrawCalls(Vec<(u64, GfxDrawCall)>),
+    ReplaceDrawCalls { timest: u64, dcs: Vec<(u64, GfxDrawCall)> },
 }
 
 pub type GraphicsEventPublisherPtr = Arc<GraphicsEventPublisher>;
@@ -590,7 +592,10 @@ impl Stage {
             ctx,
             pipeline,
             white_texture,
-            draw_calls: HashMap::from([(0, DrawCall { instrs: vec![], dcs: vec![], z_index: 0 })]),
+            draw_calls: HashMap::from([(
+                0,
+                DrawCall { instrs: vec![], dcs: vec![], z_index: 0, timest: 0 },
+            )]),
             textures: HashMap::new(),
             buffers: HashMap::new(),
             method_rep,
@@ -612,7 +617,9 @@ impl Stage {
                 self.method_new_index_buffer(indices, sendr)
             }
             GraphicsMethod::DeleteBuffer(buffer) => self.method_delete_buffer(buffer),
-            GraphicsMethod::ReplaceDrawCalls(dcs) => self.method_replace_draw_calls(dcs),
+            GraphicsMethod::ReplaceDrawCalls { timest, dcs } => {
+                self.method_replace_draw_calls(timest, dcs)
+            }
         };
     }
 
@@ -677,13 +684,25 @@ impl Stage {
         }
         self.ctx.delete_buffer(buffer);
     }
-    fn method_replace_draw_calls(&mut self, dcs: Vec<(u64, GfxDrawCall)>) {
+    fn method_replace_draw_calls(&mut self, timest: u64, dcs: Vec<(u64, GfxDrawCall)>) {
         if DEBUG_GFXAPI {
             debug!(target: "gfx", "Invoked method: replace_draw_calls({:?})", dcs);
         }
         for (key, val) in dcs {
-            let val = val.compile(&self.textures, &self.buffers);
-            self.draw_calls.insert(key, val);
+            let val = val.compile(&self.textures, &self.buffers, timest);
+            match self.draw_calls.get_mut(&key) {
+                Some(old_val) => {
+                    // Only replace the draw call if it is more recent
+                    if old_val.timest < timest {
+                        *old_val = val;
+                    } else if DEBUG_GFXAPI {
+                        debug!(target: "gfx", "Rejected stale draw_call {key}: {val:?}");
+                    }
+                }
+                None => {
+                    self.draw_calls.insert(key, val);
+                }
+            }
         }
     }
 }

+ 8 - 4
bin/darkwallet/src/ui/chatedit.rs

@@ -48,7 +48,7 @@ use crate::{
     pubsub::Subscription,
     scene::{MethodCallSub, Pimpl, SceneNodePtr, SceneNodeWeak},
     text::{self, Glyph, GlyphPositionIter, TextShaperPtr},
-    util::{enumerate_ref, is_whitespace, min_f32, zip4},
+    util::{enumerate_ref, is_whitespace, min_f32, unixtime, zip4},
     ExecutorPtr,
 };
 
@@ -1522,6 +1522,7 @@ impl ChatEdit {
             }
             _ => {}
         }
+        debug!(target: "ui::chatedit", "handle touch end showing keyboard");
         window::show_keyboard(true);
         true
     }
@@ -1630,21 +1631,23 @@ impl ChatEdit {
     }
 
     async fn redraw(&self) {
+        let timest = unixtime();
         //debug!(target: "ui::chatedit", "redraw()");
         let Some(draw_update) = self.make_draw_calls() else {
             error!(target: "ui::chatedit", "Text failed to draw");
             return;
         };
-        self.render_api.replace_draw_calls(draw_update.draw_calls);
+        self.render_api.replace_draw_calls(timest, draw_update.draw_calls);
     }
 
     fn redraw_cursor(&self) {
+        let timest = unixtime();
         let cursor_instrs = self.get_cursor_instrs();
         let draw_calls = vec![(
             self.cursor_dc_key,
             GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: self.z_index.get() },
         )];
-        self.render_api.replace_draw_calls(draw_calls);
+        self.render_api.replace_draw_calls(timest, draw_calls);
     }
 
     fn get_cursor_instrs(&self) -> Vec<GfxDrawInstruction> {
@@ -1748,7 +1751,8 @@ impl ChatEdit {
 
 impl Drop for ChatEdit {
     fn drop(&mut self) {
-        self.render_api.replace_draw_calls(vec![(self.text_dc_key, Default::default())]);
+        self.render_api
+            .replace_draw_calls(unixtime(), vec![(self.text_dc_key, Default::default())]);
     }
 }
 

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

@@ -51,7 +51,7 @@ use crate::{
     pubsub::Subscription,
     scene::{MethodCallSub, Pimpl, SceneNodeWeak},
     text::{self, Glyph, GlyphPositionIter, TextShaperPtr},
-    util::{enumerate, is_whitespace},
+    util::{enumerate, is_whitespace, unixtime},
     ExecutorPtr,
 };
 
@@ -661,6 +661,7 @@ impl ChatView {
     }
 
     async fn redraw_cached(&self, msgbuf: &mut MessageBuffer) {
+        let timest = unixtime();
         let rect = self.rect.get();
 
         let mut mesh_instrs = self.get_meshes(msgbuf, &rect).await;
@@ -671,7 +672,7 @@ impl ChatView {
         let draw_calls =
             vec![(self.dc_key, GfxDrawCall { instrs, dcs: vec![], z_index: self.z_index.get() })];
 
-        self.render_api.replace_draw_calls(draw_calls);
+        self.render_api.replace_draw_calls(timest, draw_calls);
     }
 
     /// Invalidates cache and redraws everything
@@ -1002,6 +1003,6 @@ impl UIObject for ChatView {
 
 impl Drop for ChatView {
     fn drop(&mut self) {
-        self.render_api.replace_draw_calls(vec![(self.dc_key, Default::default())]);
+        self.render_api.replace_draw_calls(unixtime(), vec![(self.dc_key, Default::default())]);
     }
 }

+ 7 - 4
bin/darkwallet/src/ui/editbox/mod.rs

@@ -45,7 +45,7 @@ use crate::{
     pubsub::Subscription,
     scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
     text::{self, Glyph, GlyphPositionIter, TextShaperPtr},
-    util::{enumerate_ref, is_whitespace, zip3},
+    util::{enumerate_ref, is_whitespace, unixtime, zip3},
     ExecutorPtr,
 };
 
@@ -1161,6 +1161,7 @@ impl EditBox {
     }
 
     async fn redraw(&self) {
+        let timest = unixtime();
         debug!(target: "ui::editbox", "redraw()");
 
         let parent_rect = self.parent_rect.lock().unwrap().unwrap().clone();
@@ -1171,10 +1172,11 @@ impl EditBox {
             return;
         };
 
-        self.render_api.replace_draw_calls(draw_update.draw_calls);
+        self.render_api.replace_draw_calls(timest, draw_update.draw_calls);
     }
 
     fn redraw_cursor(&self) {
+        let timest = unixtime();
         let cursor_instrs = self.get_cursor_instrs();
 
         let draw_calls = vec![(
@@ -1182,7 +1184,7 @@ impl EditBox {
             GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: self.z_index.get() },
         )];
 
-        self.render_api.replace_draw_calls(draw_calls);
+        self.render_api.replace_draw_calls(timest, draw_calls);
     }
 
     fn get_cursor_instrs(&self) -> Vec<GfxDrawInstruction> {
@@ -1248,7 +1250,8 @@ impl EditBox {
 
 impl Drop for EditBox {
     fn drop(&mut self) {
-        self.render_api.replace_draw_calls(vec![(self.text_dc_key, Default::default())]);
+        self.render_api
+            .replace_draw_calls(unixtime(), vec![(self.text_dc_key, Default::default())]);
     }
 }
 

+ 4 - 2
bin/darkwallet/src/ui/emoji_picker/mod.rs

@@ -38,6 +38,7 @@ use crate::{
     prop::{PropertyFloat32, PropertyPtr, PropertyRect, PropertyStr, PropertyUint32, Role},
     scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
     text::{self, GlyphPositionIter, TextShaper, TextShaperPtr},
+    util::unixtime,
     ExecutorPtr,
 };
 
@@ -234,13 +235,14 @@ impl EmojiPicker {
     }
 
     fn redraw(&self) {
+        let timest = unixtime();
         let Some(parent_rect) = self.parent_rect.lock().unwrap().clone() else { return };
 
         let Some(draw_update) = self.get_draw_calls(parent_rect) else {
             error!(target: "ui::emoji_picker", "Emoji picker failed to draw");
             return;
         };
-        self.render_api.replace_draw_calls(draw_update.draw_calls);
+        self.render_api.replace_draw_calls(timest, draw_update.draw_calls);
         d!("replace draw calls done");
     }
 
@@ -425,6 +427,6 @@ impl UIObject for EmojiPicker {
 
 impl Drop for EmojiPicker {
     fn drop(&mut self) {
-        self.render_api.replace_draw_calls(vec![(self.dc_key, Default::default())]);
+        self.render_api.replace_draw_calls(unixtime(), vec![(self.dc_key, Default::default())]);
     }
 }

+ 4 - 2
bin/darkwallet/src/ui/image.rs

@@ -32,6 +32,7 @@ use crate::{
     mesh::{MeshBuilder, MeshInfo, COLOR_WHITE},
     prop::{PropertyPtr, PropertyRect, PropertyStr, PropertyUint32, Role},
     scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
+    util::unixtime,
     ExecutorPtr,
 };
 
@@ -125,13 +126,14 @@ impl Image {
     }
 
     async fn redraw(self: Arc<Self>) {
+        let timest = unixtime();
         let Some(parent_rect) = self.parent_rect.lock().unwrap().clone() else { return };
 
         let Some(draw_update) = self.get_draw_calls(parent_rect).await else {
             error!(target: "ui::image", "Image failed to draw");
             return;
         };
-        self.render_api.replace_draw_calls(draw_update.draw_calls);
+        self.render_api.replace_draw_calls(timest, draw_update.draw_calls);
         debug!(target: "ui::image", "replace draw calls done");
     }
 
@@ -208,6 +210,6 @@ impl UIObject for Image {
 
 impl Drop for Image {
     fn drop(&mut self) {
-        self.render_api.replace_draw_calls(vec![(self.dc_key, Default::default())]);
+        self.render_api.replace_draw_calls(unixtime(), vec![(self.dc_key, Default::default())]);
     }
 }

+ 11 - 7
bin/darkwallet/src/ui/layer.rs

@@ -27,6 +27,7 @@ use crate::{
     gfx::{GfxDrawCall, GfxDrawInstruction, Point, Rectangle, RenderApi},
     prop::{PropertyBool, PropertyFloat32, PropertyPtr, PropertyRect, PropertyUint32, Role},
     scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
+    util::unixtime,
     ExecutorPtr,
 };
 
@@ -51,9 +52,8 @@ pub struct Layer {
 
 impl Layer {
     pub async fn new(node: SceneNodeWeak, render_api: RenderApi, ex: ExecutorPtr) -> Pimpl {
-        debug!(target: "ui::layer", "Layer::new()");
-
         let node_ref = &node.upgrade().unwrap();
+        debug!(target: "ui::layer", "Layer::new({node_ref:?})");
         let is_visible = PropertyBool::wrap(node_ref, Role::Internal, "is_visible", 0).unwrap();
         let rect = PropertyRect::wrap(node_ref, Role::Internal, "rect").unwrap();
         let z_index = PropertyUint32::wrap(node_ref, Role::Internal, "z_index", 0).unwrap();
@@ -83,20 +83,22 @@ impl Layer {
     }
 
     async fn redraw(self: Arc<Self>) {
+        let timest = unixtime();
+        debug!(target: "ui::layer", "Layer::redraw({:?})", self.node.upgrade().unwrap());
         let Some(parent_rect) = self.parent_rect.lock().unwrap().clone() else { return };
 
         let Some(draw_update) = self.get_draw_calls(parent_rect).await else {
             error!(target: "ui::layer", "Layer failed to draw");
             return;
         };
-        self.render_api.replace_draw_calls(draw_update.draw_calls);
-        debug!(target: "ui::layer", "replace draw calls done");
+        self.render_api.replace_draw_calls(timest, draw_update.draw_calls);
+        debug!(target: "ui::layer", "Layer::redraw({:?}) DONE [timest={timest}]", self.node.upgrade().unwrap());
     }
 
     async fn get_draw_calls(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
-        debug!(target: "ui::layer", "Layer::get_draw_calls()");
         self.rect.eval(&parent_rect).ok()?;
         let rect = self.rect.get();
+        debug!(target: "ui::layer", "Layer::get_draw_calls() [rect={rect:?}, dc={}]", self.dc_key);
 
         // Apply viewport
 
@@ -109,7 +111,7 @@ impl Layer {
             for child in self.get_children() {
                 let obj = get_ui_object3(&child);
                 let Some(mut draw_update) = obj.draw(rect).await else {
-                    debug!(target: "ui::layer", "Skipped draw() of {child:?}");
+                    debug!(target: "ui::layer", "Skipped draw for {child:?}");
                     continue
                 };
 
@@ -169,7 +171,9 @@ impl UIObject for Layer {
         }
         */
 
-        self.get_draw_calls(parent_rect).await
+        let update = self.get_draw_calls(parent_rect).await;
+        debug!(target: "ui::layer", "Layer::draw({:?}) DONE", self.node.upgrade().unwrap());
+        update
     }
 
     async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) -> bool {

+ 1 - 1
bin/darkwallet/src/ui/mod.rs

@@ -159,7 +159,7 @@ impl<T: Send + Sync + 'static> OnModify<T> {
                     }
                 }
 
-                //debug!(target: "app", "Property '{}':{}/'{}' modified", node_name, node_id, prop_name);
+                debug!(target: "app", "Property '{}':{}/'{}' modified", node_name, node_id, prop_name);
 
                 let Some(self_) = me.upgrade() else {
                     // Should not happen

+ 6 - 4
bin/darkwallet/src/ui/text.rs

@@ -32,6 +32,7 @@ use crate::{
     },
     scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
     text::{self, GlyphPositionIter, TextShaper, TextShaperPtr},
+    util::unixtime,
     ExecutorPtr,
 };
 
@@ -150,18 +151,19 @@ impl Text {
     }
 
     async fn redraw(self: Arc<Self>) {
+        let timest = unixtime();
+        debug!(target: "ui::text", "Text::redraw({:?})", self.node.upgrade().unwrap());
         let Some(parent_rect) = self.parent_rect.lock().unwrap().clone() else { return };
 
         let Some(draw_update) = self.get_draw_calls(parent_rect).await else {
             error!(target: "ui::text", "Text failed to draw");
             return;
         };
-        self.render_api.replace_draw_calls(draw_update.draw_calls);
+        self.render_api.replace_draw_calls(timest, draw_update.draw_calls);
         debug!(target: "ui::text", "replace draw calls done");
     }
 
     async fn get_draw_calls(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
-        debug!(target: "ui::text", "Text::get_draw_calls()");
         self.rect.eval(&parent_rect).ok()?;
         let rect = self.rect.get();
 
@@ -217,7 +219,7 @@ impl UIObject for Text {
     }
 
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
-        debug!(target: "ui::text", "Text::draw()");
+        debug!(target: "ui::text", "Text::draw({:?})", self.node.upgrade().unwrap());
         *self.parent_rect.lock().unwrap() = Some(parent_rect);
         self.get_draw_calls(parent_rect).await
     }
@@ -225,6 +227,6 @@ impl UIObject for Text {
 
 impl Drop for Text {
     fn drop(&mut self) {
-        self.render_api.replace_draw_calls(vec![(self.dc_key, Default::default())]);
+        self.render_api.replace_draw_calls(unixtime(), vec![(self.dc_key, Default::default())]);
     }
 }

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

@@ -29,7 +29,7 @@ use crate::{
     mesh::Color,
     prop::{PropertyBool, PropertyFloat32, PropertyPtr, PropertyRect, PropertyUint32, Role},
     scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
-    util::enumerate,
+    util::{enumerate, unixtime},
     ExecutorPtr,
 };
 
@@ -91,13 +91,15 @@ impl VectorArt {
     }
 
     async fn redraw(self: Arc<Self>) {
+        let timest = unixtime();
+        debug!(target: "ui::vector_art", "VectorArt::redraw({:?})", self.node.upgrade().unwrap());
         let Some(parent_rect) = self.parent_rect.lock().unwrap().clone() else { return };
 
         let Some(draw_update) = self.get_draw_calls(parent_rect).await else {
             error!(target: "ui::vector_art", "Mesh failed to draw");
             return;
         };
-        self.render_api.replace_draw_calls(draw_update.draw_calls);
+        self.render_api.replace_draw_calls(timest, draw_update.draw_calls);
         //debug!(target: "ui::vector_art", "replace draw calls done");
     }
 
@@ -171,6 +173,6 @@ impl UIObject for VectorArt {
 
 impl Drop for VectorArt {
     fn drop(&mut self) {
-        self.render_api.replace_draw_calls(vec![(self.dc_key, Default::default())]);
+        self.render_api.replace_draw_calls(unixtime(), vec![(self.dc_key, Default::default())]);
     }
 }

+ 4 - 1
bin/darkwallet/src/ui/win.rs

@@ -26,6 +26,7 @@ use crate::{
     prop::{PropertyDimension, PropertyFloat32, PropertyPtr, Role},
     pubsub::Subscription,
     scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
+    util::unixtime,
     ExecutorPtr,
 };
 
@@ -439,6 +440,8 @@ impl Window {
     }
 
     pub async fn draw(&self) {
+        let timest = unixtime();
+
         let local = self.screen_size.get() / self.scale.get();
         let rect = Rectangle::from([0., 0., local.w, local.h]);
         debug!(target: "ui::win", "Window::draw({rect:?})");
@@ -465,7 +468,7 @@ impl Window {
         draw_calls.push((0, dc));
         //debug!(target: "ui::win", "  => {:?}", draw_calls);
 
-        self.render_api.replace_draw_calls(draw_calls);
+        self.render_api.replace_draw_calls(timest, draw_calls);
 
         debug!(target: "ui::win", "Window::draw() - replaced draw call");
     }

+ 7 - 0
bin/darkwallet/src/util.rs

@@ -17,6 +17,7 @@
  */
 
 use colored::Colorize;
+use std::time::{SystemTime, UNIX_EPOCH};
 
 pub fn is_whitespace(s: &str) -> bool {
     s.chars().all(char::is_whitespace)
@@ -30,6 +31,12 @@ pub fn min_f32(x: f32, y: f32) -> f32 {
     }
 }
 
+pub fn unixtime() -> u64 {
+    let timest = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis();
+    assert!(timest < std::u64::MAX as u128);
+    timest as u64
+}
+
 #[allow(dead_code)]
 pub fn ansi_texture(width: usize, height: usize, data: &Vec<u8>) -> String {
     let mut out = String::new();