Browse Source

app: make video load frames in a thread but wait on it so it can be ez parallelized after

darkfi 11 months ago
parent
commit
bf89cc7da8
3 changed files with 55 additions and 12 deletions
  1. 1 1
      bin/app/src/app/schema/test.rs
  2. 16 1
      bin/app/src/gfx/anim.rs
  3. 38 10
      bin/app/src/ui/video.rs

+ 1 - 1
bin/app/src/app/schema/test.rs

@@ -255,7 +255,7 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
     prop.set_f32(atom, Role::App, 1, 400.).unwrap();
     prop.set_f32(atom, Role::App, 2, 600.).unwrap();
     prop.set_f32(atom, Role::App, 3, 600.).unwrap();
-    node.set_property_str(atom, Role::App, "path", "assets/forest/forest_{frame}.jpg").unwrap();
+    node.set_property_str(atom, Role::App, "path", "assets/forest/forest_{frame}.png").unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 1).unwrap();
     node.set_property_u32(atom, Role::App, "length", 357).unwrap();
     let node = node.setup(|me| Video::new(me, app.render_api.clone())).await;

+ 16 - 1
bin/app/src/gfx/anim.rs

@@ -24,7 +24,7 @@ use std::{
     io::Write,
     sync::{
         atomic::{AtomicU32, Ordering},
-        Arc,
+        Arc, RwLock,
     },
 };
 
@@ -33,6 +33,19 @@ use super::{DrawCall, GfxBufferId, GfxDrawCall, GfxTextureId};
 // This can be in instruction but also implement encodable
 // maybe just remove trax?
 
+/*
+type GfxFrameOpt = Arc<RwLock<Option<GfxSequenceAnimationFrame>>>;
+
+pub struct SequenceAnimBuffer {
+    frames: Vec<GfxFrameOpt>
+}
+
+impl SequenceAnimBuffer {
+    pub fn new(len: usize) -> Self {
+    }
+}
+*/
+
 #[derive(Debug, Clone, SerialEncodable)]
 pub struct GfxSequenceAnimation {
     oneshot: bool,
@@ -41,6 +54,7 @@ pub struct GfxSequenceAnimation {
 
 impl GfxSequenceAnimation {
     pub fn new(oneshot: bool, frames: Vec<GfxSequenceAnimationFrame>) -> Self {
+        //let frames = frames.into_iter().map(|f| Arc::new(RwLock::new(
         Self { oneshot, frames }
     }
 
@@ -99,6 +113,7 @@ impl AsyncEncodable for GfxSequenceAnimationFrame {
 pub(super) struct SequenceAnimation {
     oneshot: bool,
     frames: Vec<SequenceAnimationFrame>,
+    //incoming_frames: Vec<Arc<RwLock<Option<GfxSequenceAnimationFrame>>>>,
     state: RefCell<SequenceAnimationState>,
 }
 

+ 38 - 10
bin/app/src/ui/video.rs

@@ -20,7 +20,13 @@ use async_trait::async_trait;
 use image::ImageReader;
 use parking_lot::Mutex as SyncMutex;
 use rand::{rngs::OsRng, Rng};
-use std::{io::Cursor, sync::Arc};
+use std::{
+    io::Cursor,
+    sync::{
+        atomic::{AtomicBool, Ordering},
+        Arc,
+    },
+};
 
 use crate::{
     gfx::{
@@ -45,6 +51,7 @@ pub struct Video {
     node: SceneNodeWeak,
     render_api: RenderApi,
     tasks: SyncMutex<Vec<smol::Task<()>>>,
+    stop_load: Arc<AtomicBool>,
 
     textures: SyncMutex<Vec<ManagedTexturePtr>>,
     dc_key: u64,
@@ -75,6 +82,7 @@ impl Video {
             node,
             render_api,
             tasks: SyncMutex::new(vec![]),
+            stop_load: Arc::new(AtomicBool::new(false)),
 
             textures: SyncMutex::new(vec![]),
             dc_key: OsRng.gen(),
@@ -98,20 +106,40 @@ impl Video {
     }
 
     fn load_textures(&self) {
+        let (sendr, recvr) = async_channel::bounded(1);
         let len = self.len.get();
-        let mut textures = Vec::with_capacity(len as usize);
         let path_fmt = self.path.get();
-        for i in 0..len {
-            t!("i = {i}");
-            let path = path_fmt.replace("{frame}", &format!("{i:#03}"));
-            let texture = self.load_texture(path);
-            textures.push(texture);
-        }
+        let render_api = self.render_api.clone();
+        let stop_load = self.stop_load.clone();
+        let handle = std::thread::spawn(move || {
+            let mut textures = Vec::with_capacity(len as usize);
+            let instant = std::time::Instant::now();
+            for i in 0..len {
+                // Stop loading instantly
+                if stop_load.load(Ordering::Relaxed) {
+                    return
+                }
+                t!("i = {i}");
+                let path = path_fmt.replace("{frame}", &format!("{i:#03}"));
+                let texture = Self::load_texture(path, &render_api);
+                textures.push(texture);
+            }
+            t!("elapsed = {:?}", instant.elapsed());
+            sendr.send_blocking(textures).unwrap();
+        });
+
+        // Temp here
+        let Ok(textures) = recvr.recv_blocking() else {
+            let node_ref = &self.node.upgrade().unwrap();
+            t!("loading textures was stopped {node_ref:?}");
+            return
+        };
+        assert!(handle.is_finished());
 
         *self.textures.lock() = textures;
     }
 
-    fn load_texture(&self, path: String) -> ManagedTexturePtr {
+    fn load_texture(path: String, render_api: &RenderApi) -> ManagedTexturePtr {
         // TODO we should NOT use panic here
         let data = Arc::new(SyncMutex::new(vec![]));
         let data2 = data.clone();
@@ -133,7 +161,7 @@ impl Video {
         let height = img.height() as u16;
         let bmp = img.into_raw();
 
-        self.render_api.new_texture(width, height, bmp, gfxtag!("img"))
+        render_api.new_texture(width, height, bmp, gfxtag!("img"))
     }
 
     async fn redraw(self: Arc<Self>, batch: BatchGuardPtr) {