mirror of
https://github.com/itsjunetime/tdf.git
synced 2026-06-01 23:51:46 -04:00
- 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:
@@ -2,6 +2,9 @@ mod utils;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
#[cfg(feature = "tracing")]
|
||||
console_subscriber::init();
|
||||
|
||||
let file = std::env::args()
|
||||
.nth(1)
|
||||
.expect("Please enter a file to profile");
|
||||
|
||||
+32
-21
@@ -1,34 +1,45 @@
|
||||
mod utils;
|
||||
|
||||
use utils::render_doc;
|
||||
use utils::{render_doc, render_first_page};
|
||||
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
|
||||
fn render_dict(c: &mut Criterion) {
|
||||
c.bench_function(
|
||||
"example dictionary",
|
||||
|b| b.iter(||
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(render_doc("./benches/example_dictionary.pdf"))
|
||||
)
|
||||
);
|
||||
const FILES: [&str; 2] = [
|
||||
"./benches/example_dictionary.pdf",
|
||||
"./benches/adobe_example.pdf"
|
||||
];
|
||||
|
||||
fn render_full(c: &mut Criterion) {
|
||||
for file in FILES {
|
||||
c.bench_with_input(
|
||||
BenchmarkId::new("render_full", file),
|
||||
&file,
|
||||
|b, &file| b.iter(||
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(render_doc(file))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_example(c: &mut Criterion) {
|
||||
c.bench_function(
|
||||
"adobe-provided sample",
|
||||
|b| b.iter(||
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(render_doc("./benches/adobe_example.pdf"))
|
||||
)
|
||||
);
|
||||
fn render_to_first_page(c: &mut Criterion) {
|
||||
for file in FILES {
|
||||
c.bench_with_input(
|
||||
BenchmarkId::new("render_first_page", file),
|
||||
&file,
|
||||
|b, &file| b.iter(||
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(render_first_page(file))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
name = benches;
|
||||
config = Criterion::default().sample_size(10);
|
||||
targets = render_dict, render_example
|
||||
targets = render_full, render_to_first_page
|
||||
);
|
||||
criterion_main!(benches);
|
||||
|
||||
+97
-40
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user