- Significantly improved time to render full document

- Added support for debugging with tokio-console through tracing feature
- Added extra benchmark for checking time to render first page
- Removed unwraps to just make background loops return and terminate if something goes wrong
- Modularize rendering somewhat
This commit is contained in:
itsjunetime
2024-06-04 15:46:25 -06:00
parent a86730b8da
commit 5825849434
8 changed files with 846 additions and 133 deletions
+97 -40
View File
@@ -1,13 +1,55 @@
use std::{hint::black_box, path::Path};
use crossterm::terminal::WindowSize;
use flume::{unbounded, Sender};
use flume::{r#async::RecvStream, unbounded, Sender};
use ratatui::layout::Rect;
use ratatui_image::picker::{Picker, ProtocolType};
use tdf::{converter::{run_conversion_loop, ConvertedPage, ConverterMsg}, renderer::{fill_default, start_rendering, RenderError, RenderInfo, RenderNotif}};
use futures_util::stream::StreamExt as _;
pub async fn render_doc(path: impl AsRef<Path>) {
fn handle_renderer_msg(
msg: Result<RenderInfo, RenderError>,
pages: &mut Vec<Option<ConvertedPage>>,
to_converter_tx: &mut Sender<tdf::converter::ConverterMsg>,
) {
match msg {
Ok(RenderInfo::NumPages(num)) => {
fill_default(pages, num);
to_converter_tx.send(ConverterMsg::NumPages(num)).unwrap();
},
Ok(RenderInfo::Page(info)) => to_converter_tx.send(ConverterMsg::AddImg(info)).unwrap(),
Err(e) => panic!("Got error from renderer: {e:?}")
}
}
fn handle_converter_msg(
msg: Result<ConvertedPage, RenderError>,
pages: &mut [Option<ConvertedPage>],
to_converter_tx: &mut Sender<ConverterMsg>
) {
let page = msg.expect("Got error from converter");
let num = page.num;
pages[num] = Some(page);
let num_got = pages.iter()
.filter(|p| p.is_some())
.count();
// we have to tell it to jump to a certain page so that it will actually render it (since
// it only renders fanning out from the page that we currently have selected)
to_converter_tx.send(ConverterMsg::GoToPage(num_got)).unwrap();
}
struct RenderState {
from_render_rx: RecvStream<'static, Result<RenderInfo, RenderError>>,
from_converter_rx: RecvStream<'static, Result<ConvertedPage, RenderError>>,
pages: Vec<Option<ConvertedPage>>,
to_converter_tx: Sender<ConverterMsg>,
to_render_tx: Sender<RenderNotif>
}
fn start_all_rendering(path: impl AsRef<Path>) -> RenderState {
let pathbuf = path.as_ref().canonicalize().unwrap();
let str_path = format!("file://{}", pathbuf.into_os_string().to_string_lossy());
@@ -28,7 +70,7 @@ pub async fn render_doc(path: impl AsRef<Path>) {
start_rendering(str_path, to_main_tx, from_main_rx, size)
});
let (mut to_converter_tx, from_main_rx) = unbounded();
let (to_converter_tx, from_main_rx) = unbounded();
let (to_main_tx, from_converter_rx) = unbounded();
let mut picker = Picker::new(font_size);
@@ -36,41 +78,7 @@ pub async fn render_doc(path: impl AsRef<Path>) {
tokio::spawn(run_conversion_loop(to_main_tx, from_main_rx, picker));
let mut pages: Vec<Option<ConvertedPage>> = Vec::new();
fn handle_renderer_msg(
msg: Result<RenderInfo, RenderError>,
pages: &mut Vec<Option<ConvertedPage>>,
to_converter_tx: &mut Sender<tdf::converter::ConverterMsg>,
) {
match msg {
Ok(RenderInfo::NumPages(num)) => {
fill_default(pages, num);
to_converter_tx.send(ConverterMsg::NumPages(num)).unwrap();
},
Ok(RenderInfo::Page(info)) => to_converter_tx.send(ConverterMsg::AddImg(info)).unwrap(),
Err(e) => panic!("Got error from renderer: {e:?}")
}
}
fn handle_converter_msg(
msg: Result<ConvertedPage, RenderError>,
pages: &mut [Option<ConvertedPage>],
to_converter_tx: &mut Sender<ConverterMsg>
) {
let page = msg.expect("Got error from converter");
let num = page.num;
pages[num] = Some(page);
let num_got = pages.iter()
.filter(|p| p.is_some())
.count();
// we have to tell it to jump to a certain page so that it will actually render it (since
// it only renders fanning out from the page that we currently have selected)
to_converter_tx.send(ConverterMsg::GoToPage(num_got)).unwrap();
}
let pages: Vec<Option<ConvertedPage>> = Vec::new();
let main_area = Rect {
x: 0,
@@ -80,8 +88,26 @@ pub async fn render_doc(path: impl AsRef<Path>) {
};
to_render_tx.send(RenderNotif::Area(main_area)).unwrap();
let mut from_render_rx = from_render_rx.into_stream();
let mut from_converter_rx = from_converter_rx.into_stream();
let from_render_rx = from_render_rx.into_stream();
let from_converter_rx = from_converter_rx.into_stream();
RenderState {
from_render_rx,
from_converter_rx,
pages,
to_converter_tx,
to_render_tx
}
}
pub async fn render_doc(path: impl AsRef<Path>) {
let RenderState {
mut from_render_rx,
mut from_converter_rx,
mut pages,
mut to_converter_tx,
to_render_tx
} = start_all_rendering(path);
while pages.is_empty() || pages.iter().any(|p| p.is_none()) {
tokio::select! {
@@ -95,4 +121,35 @@ pub async fn render_doc(path: impl AsRef<Path>) {
}
black_box(pages);
// we want to make sure this is kept around until the end of this function, or else the other
// thread will see that this is disconnected and think that we're done communicating with them
drop(to_render_tx);
}
#[cfg(test)]
pub async fn render_first_page(path: impl AsRef<Path>) {
let RenderState {
mut from_render_rx,
mut from_converter_rx,
mut pages,
mut to_converter_tx,
to_render_tx
} = start_all_rendering(path);
// we only want to render until the first page is ready to be printed
while pages.is_empty() {
tokio::select! {
Some(renderer_msg) = from_render_rx.next() => {
handle_renderer_msg(renderer_msg, &mut pages, &mut to_converter_tx);
},
Some(converter_msg) = from_converter_rx.next() => {
handle_converter_msg(converter_msg, &mut pages, &mut to_converter_tx);
}
}
}
black_box(pages);
// we want to make sure this is kept around until the end of this function, or else the other
// thread will see that this is disconnected and think that we're done communicating with them
drop(to_render_tx);
}