wip: solved the preview performance regression

This commit is contained in:
Alexandre Pasmantier 2025-05-05 19:45:54 +02:00
parent fc3f24fd89
commit 07ad8d760b
2 changed files with 10 additions and 42 deletions

View File

@ -1,6 +1,5 @@
use crate::channels::cable::prototypes::CableChannelPrototype; use crate::channels::cable::prototypes::CableChannelPrototype;
use crate::preview::{Preview, PreviewContent}; use crate::preview::{Preview, PreviewContent};
use crate::utils::cache::RingSet;
use crate::utils::command::shell_command; use crate::utils::command::shell_command;
use crate::{ use crate::{
channels::{entry::Entry, preview::PreviewCommand}, channels::{entry::Entry, preview::PreviewCommand},
@ -16,34 +15,25 @@ use tracing::debug;
#[derive(Debug)] #[derive(Debug)]
pub struct Previewer { pub struct Previewer {
cache: Arc<Mutex<PreviewCache>>, cache: Arc<Mutex<PreviewCache>>,
requests: RingSet<Entry>,
concurrent_preview_tasks: Arc<AtomicU8>, concurrent_preview_tasks: Arc<AtomicU8>,
in_flight_previews: Arc<Mutex<FxHashSet<String>>>, in_flight_previews: Arc<Mutex<FxHashSet<String>>>,
command: PreviewCommand, command: PreviewCommand,
} }
const REQUEST_STACK_SIZE: usize = 10;
impl Previewer { impl Previewer {
// we could use a target scroll here to make the previewer // we could use a target scroll here to make the previewer
// faster, but since it's already running in the background and quite // faster, but since it's already running in the background and quite
// fast for most standard file sizes, plus we're caching the previews, // fast for most standard file sizes, plus we're caching the previews,
// I'm not sure the extra complexity is worth it. // I'm not sure the extra complexity is worth it.
pub fn handle_request(&mut self, entry: &Entry) -> Option<Arc<Preview>> { pub fn request(&mut self, entry: &Entry) -> Option<Arc<Preview>> {
// check if we have a preview in cache for the current request // check if we have a preview in cache for the current request
if let Some(preview) = self.cached(entry) { if let Some(preview) = self.cached(entry) {
return Some(preview); return Some(preview);
} }
// otherwise, if we haven't acknowledged the request yet, acknowledge it // start a background task to compute the preview
self.requests.push(entry.clone()); self.preview(entry);
// lookup request stack and return the most recent preview available
for entry in self.requests.back_to_front() {
if let Some(preview) = self.preview(&entry) {
return Some(preview);
}
}
None None
} }
} }
@ -54,7 +44,6 @@ impl Previewer {
pub fn new(command: PreviewCommand) -> Self { pub fn new(command: PreviewCommand) -> Self {
Previewer { Previewer {
cache: Arc::new(Mutex::new(PreviewCache::default())), cache: Arc::new(Mutex::new(PreviewCache::default())),
requests: RingSet::with_capacity(REQUEST_STACK_SIZE),
concurrent_preview_tasks: Arc::new(AtomicU8::new(0)), concurrent_preview_tasks: Arc::new(AtomicU8::new(0)),
in_flight_previews: Arc::new(Mutex::new(FxHashSet::default())), in_flight_previews: Arc::new(Mutex::new(FxHashSet::default())),
command, command,
@ -65,18 +54,7 @@ impl Previewer {
self.cache.lock().get(&entry.name) self.cache.lock().get(&entry.name)
} }
pub fn preview(&mut self, entry: &Entry) -> Option<Arc<Preview>> { pub fn preview(&mut self, entry: &Entry) {
if let Some(preview) = self.cached(entry) {
Some(preview)
} else {
// preview is not in cache, spawn a task to compute the preview
debug!("Preview cache miss for {:?}", entry.name);
self.handle_preview_request(entry);
None
}
}
pub fn handle_preview_request(&mut self, entry: &Entry) {
if self.in_flight_previews.lock().contains(&entry.name) { if self.in_flight_previews.lock().contains(&entry.name) {
debug!("Preview already in flight for {:?}", entry.name); debug!("Preview already in flight for {:?}", entry.name);
return; return;

View File

@ -8,9 +8,7 @@ use crate::{
}, },
entry::Entry, entry::Entry,
remote_control::RemoteControl, remote_control::RemoteControl,
// stdin::Channel as StdinChannel, OnAir, TelevisionChannel,
OnAir,
TelevisionChannel,
}, },
config::{Config, Theme}, config::{Config, Theme},
draw::{ChannelState, Ctx, TvState}, draw::{ChannelState, Ctx, TvState},
@ -391,15 +389,11 @@ impl Television {
// FIXME: this is probably redundant with the channel supporting previews // FIXME: this is probably redundant with the channel supporting previews
&& self.previewer.is_some() && self.previewer.is_some()
{ {
// preview content // avoid sending unnecessary requests to the previewer
if let Some(preview) = self if self.preview_state.preview.title != selected_entry.name {
.previewer if let Some(preview) =
.as_mut() self.previewer.as_mut().unwrap().request(selected_entry)
.unwrap() {
.handle_request(selected_entry)
{
// only update if the preview content has changed
if self.preview_state.preview.title != preview.title {
self.preview_state.update( self.preview_state.update(
preview, preview,
// scroll to center the selected entry // scroll to center the selected entry
@ -576,15 +570,12 @@ impl Television {
self.handle_input_action(action); self.handle_input_action(action);
} }
Action::SelectNextEntry => { Action::SelectNextEntry => {
self.preview_state.reset();
self.select_next_entry(1); self.select_next_entry(1);
} }
Action::SelectPrevEntry => { Action::SelectPrevEntry => {
self.preview_state.reset();
self.select_prev_entry(1); self.select_prev_entry(1);
} }
Action::SelectNextPage => { Action::SelectNextPage => {
self.preview_state.reset();
self.select_next_entry( self.select_next_entry(
self.ui_state self.ui_state
.layout .layout
@ -595,7 +586,6 @@ impl Television {
); );
} }
Action::SelectPrevPage => { Action::SelectPrevPage => {
self.preview_state.reset();
self.select_prev_entry( self.select_prev_entry(
self.ui_state self.ui_state
.layout .layout