From 680a841e3fb24c194a62ba6e3c63d8bafc633640 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 20 Jan 2025 18:53:15 +0100 Subject: [PATCH] relax Send + Sync constraint on Renderables; remove 3 format calls from render loop maybe render should have mutable access after all? --- output/src/output.rs | 8 +++---- output/src/thunk.rs | 4 ++-- tek/src/lib.rs | 51 ++++++++++++++++++++++++------------------ tui/src/tui_content.rs | 48 ++++++++++++++++++--------------------- tui/src/tui_output.rs | 2 +- 5 files changed, 58 insertions(+), 55 deletions(-) diff --git a/output/src/output.rs b/output/src/output.rs index 22dfaa8d..e9936064 100644 --- a/output/src/output.rs +++ b/output/src/output.rs @@ -21,13 +21,13 @@ pub trait Output: Send + Sync + Sized { #[inline] fn wh (&self) -> Self::Size { self.area().wh().into() } } /// Renderable with dynamic dispatch. -pub trait Render: Send + Sync { +pub trait Render { /// Compute layout. fn layout (&self, area: E::Area) -> E::Area; /// Write data to display. fn render (&self, output: &mut E); /// Perform type erasure, turning `self` into an opaque [RenderBox]. - fn boxed <'a> (self) -> RenderBox<'a, E> where Self: Sized + 'a { + fn boxed <'a> (self) -> RenderBox<'a, E> where Self: Send + Sync + Sized + 'a { Box::new(self) as RenderBox<'a, E> } } @@ -50,7 +50,7 @@ impl<'a, E: Output> Content for RenderBox<'a, E> { //fn boxed <'b> (self) -> RenderBox<'b, E> where Self: Sized + 'b { self } } /// Opaque pointer to a renderable. -pub type RenderDyn<'a, E> = dyn Render + 'a; +pub type RenderDyn<'a, E> = dyn Render + Send + Sync + 'a; /// You can render from an opaque pointer. impl<'a, E: Output> Content for &RenderDyn<'a, E> where Self: Sized { fn content (&self) -> impl Render { self.deref() } @@ -58,7 +58,7 @@ impl<'a, E: Output> Content for &RenderDyn<'a, E> where Self: Sized { fn render (&self, output: &mut E) { Render::render(self.deref(), output) } } /// Composable renderable with static dispatch. -pub trait Content: Send + Sync + Sized { +pub trait Content { /// Return a [Render]able of a specific type. fn content (&self) -> impl Render { () } /// Perform layout. By default, delegates to [Self::content]. diff --git a/output/src/thunk.rs b/output/src/thunk.rs index ce1c6084..8bcbcaa4 100644 --- a/output/src/thunk.rs +++ b/output/src/thunk.rs @@ -12,9 +12,9 @@ impl, F: Fn()->T + Send + Sync> Content for Thunk impl Render { (self.1)() } } -pub struct ThunkBox<'a, E: Output>(PhantomData, BoxBox + 'a> + Send + Sync + 'a>); +pub struct ThunkBox<'a, E: Output>(PhantomData, BoxRenderBox<'a, E> + Send + Sync + 'a>); impl<'a, E: Output> ThunkBox<'a, E> { - pub fn new (thunk: BoxBox + 'a> + Send + Sync + 'a>) -> Self { + pub fn new (thunk: BoxRenderBox<'a, E> + Send + Sync + 'a>) -> Self { Self(Default::default(), thunk) } } diff --git a/tek/src/lib.rs b/tek/src/lib.rs index 0c7958d5..7c27ccc0 100644 --- a/tek/src/lib.rs +++ b/tek/src/lib.rs @@ -151,9 +151,9 @@ impl TekCli { pub keys_scene: SourceIter<'static>, pub keys_mix: SourceIter<'static>, - pub fmt_beat: String, - pub fmt_time: String, - pub fmt_bpm: String, + pub fmt_beat: RwLock, + pub fmt_time: RwLock, + pub fmt_bpm: RwLock, } has_size!(|self: Tek|&self.size); has_clock!(|self: Tek|self.clock); @@ -229,9 +229,9 @@ impl Tek { keys_track: SourceIter(KEYS_TRACK), keys_scene: SourceIter(KEYS_SCENE), keys_mix: SourceIter(KEYS_MIX), - fmt_beat: String::with_capacity(16), - fmt_time: String::with_capacity(16), - fmt_bpm: String::with_capacity(16), + fmt_beat: String::with_capacity(16).into(), + fmt_time: String::with_capacity(16).into(), + fmt_bpm: String::with_capacity(16).into(), ..Default::default() }; tek.sync_lead(sync_lead); @@ -381,24 +381,31 @@ impl Tek { } fn view_beat_stats (&self) -> impl Content + use<'_> { let compact = self.size.w() > 80; - let clock = self.clock(); - let delta = |start: &Moment|clock.global.usec.get() - start.usec.get(); + let clock = self.clock(); + let delta = |start: &Moment|clock.global.usec.get() - start.usec.get(); + let mut fmt_beat = self.fmt_beat.write().unwrap(); + let mut fmt_time = self.fmt_time.write().unwrap(); + let mut fmt_bpm = self.fmt_bpm.write().unwrap(); + fmt_beat.clear(); + fmt_time.clear(); + fmt_bpm.clear(); if let Some(now) = clock.started.read().unwrap().as_ref().map(delta) { - clock.timebase.format_beats_1_to(&mut self.fmt_beat, clock.timebase.usecs_to_pulse(now)); - write!(&mut self.fmt_time, "{:.3}s", now/1000000.); - write!(&mut self.fmt_bpm, "{:.3}", clock.timebase.bpm.get()); + clock.timebase.format_beats_1_to(&mut*fmt_beat, clock.timebase.usecs_to_pulse(now)); + write!(&mut fmt_time, "{:.3}s", now/1000000.); + write!(&mut fmt_bpm, "{:.3}", clock.timebase.bpm.get()); } else { - write!(&mut self.fmt_beat, "-.-.--"); - write!(&mut self.fmt_time, "-.---s"); - write!(&mut self.fmt_bpm, "---.---"); + write!(&mut fmt_beat, "-.-.--"); + write!(&mut fmt_time, "-.---s"); + write!(&mut fmt_bpm, "---.---"); } let theme = ItemPalette::G[128]; - let fh = |name, value|Field(theme, name, value); - let fv = |name, value|FieldV(theme, name, value); - Either::new(compact, - row!(fh("BPM", &self.fmt_bpm), fh("Beat", &self.fmt_beat), fh("Time", &self.fmt_time)), - row!(fv("BPM", &self.fmt_bpm), fv("Beat", &self.fmt_beat), fv("Time", &self.fmt_time)) - ) + Thunk::new(move||Either::new(compact, + row!(FieldH(theme, "BPM", self.fmt_bpm.read().unwrap()), + FieldH(theme, "Beat", self.fmt_beat.read().unwrap()), + FieldH(theme, "Time", self.fmt_time.read().unwrap())), + row!(FieldV(theme, "BPM", self.fmt_bpm.read().unwrap()), + FieldV(theme, "Beat", self.fmt_beat.read().unwrap()), + FieldV(theme, "Time", self.fmt_time.read().unwrap())))) } fn view_engine_stats (&self) -> impl Content + use<'_> { let compact = self.size.w() > 80; @@ -410,12 +417,12 @@ impl Tek { let latency = move||format!("{:.1}ms", chunk as f64 / rate * 1000.); let theme = ItemPalette::G[128]; Either::new(compact, - row!(Field(theme, "SR", sr()), Field(theme, "Buf", buf()), Field(theme, "Lat", latency())), + row!(FieldH(theme, "SR", sr()), FieldH(theme, "Buf", buf()), FieldH(theme, "Lat", latency())), row!(FieldV(theme, "SR", sr()), FieldV(theme, "Buf", buf()), FieldV(theme, "Lat", latency()))) } fn view_meter <'a> (&'a self, label: &'a str, value: f32) -> impl Content + 'a { col!( - Field(ItemPalette::G[128], label, format!("{:>+9.3}", value)), + FieldH(ItemPalette::G[128], label, format!("{:>+9.3}", value)), Fixed::xy(if value >= 0.0 { 13 } else if value >= -1.0 { 12 } else if value >= -2.0 { 11 } diff --git a/tui/src/tui_content.rs b/tui/src/tui_content.rs index 72869c3d..719a83b3 100644 --- a/tui/src/tui_content.rs +++ b/tui/src/tui_content.rs @@ -20,6 +20,15 @@ impl Content for String { } } +impl Content for std::sync::RwLockReadGuard<'_, String> { + fn layout (&self, to: [u16;4]) -> [u16;4] { + Content::::layout(&**self, to) + } + fn render (&self, to: &mut TuiOut) { + Content::::render(&**self, to) + } +} + impl Content for Arc { fn layout (&self, to: [u16;4]) -> [u16;4] { to.center_xy([self.chars().count() as u16, 1]) @@ -29,43 +38,32 @@ impl Content for Arc { } } -pub struct Field(pub ItemPalette, pub T, pub U) - where T: AsRef + Send + Sync, U: AsRef + Send + Sync; - -impl Content for Field - where T: AsRef + Send + Sync, U: AsRef + Send + Sync -{ +pub struct FieldH(pub ItemPalette, pub T, pub U); +impl, U: Content> Content for FieldH { fn content (&self) -> impl Render { - let ItemPalette { darkest, dark, lighter, lightest, .. } = self.0; + let Self(ItemPalette { darkest, dark, lighter, lightest, .. }, title, value) = self; row!( Tui::fg_bg(dark.rgb, darkest.rgb, "▐"), - Tui::fg_bg(lighter.rgb, dark.rgb, Tui::bold(true, format!("{}", self.1.as_ref()))), + Tui::fg_bg(lighter.rgb, dark.rgb, Tui::bold(true, title)), Tui::fg_bg(dark.rgb, darkest.rgb, "▌"), - Tui::fg_bg(lightest.rgb, darkest.rgb, format!("{} ", self.2.as_ref())) + Tui::fg_bg(lightest.rgb, darkest.rgb, value), ) } } -pub struct FieldV(pub ItemPalette, pub T, pub U) - where T: AsRef + Send + Sync, U: AsRef + Send + Sync; - -impl Content for FieldV - where T: AsRef + Send + Sync, U: AsRef + Send + Sync -{ +pub struct FieldV(pub ItemPalette, pub T, pub U); +impl, U: Content> Content for FieldV { fn content (&self) -> impl Render { - let ItemPalette { darkest, dark, lighter, lightest, .. } = self.0; - let sep1 = Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▐")); - let sep2 = Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▌")); - let name = Tui::bg(dark.rgb, Tui::fg(lighter.rgb, - Tui::bold(true, format!("{}", self.1.as_ref())))); - let value = Tui::bg(darkest.rgb, Tui::fg(lightest.rgb, - format!(" {} ", self.2.as_ref()))); - Bsp::e(Bsp::s(row!(sep1, name, sep2), value), " ") + let Self(ItemPalette { darkest, dark, lighter, lightest, .. }, title, value) = self; + let sep1 = Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▐")); + let sep2 = Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▌")); + let title = Tui::bg(dark.rgb, Tui::fg(lighter.rgb, Tui::bold(true, title))); + let value = Tui::bg(darkest.rgb, Tui::fg(lightest.rgb, value)); + Bsp::e(Bsp::s(row!(sep1, title, sep2), value), " ") } } pub struct Repeat<'a>(pub &'a str); - impl Content for Repeat<'_> { fn layout (&self, to: [u16;4]) -> [u16;4] { to @@ -85,7 +83,6 @@ impl Content for Repeat<'_> { } pub struct RepeatV<'a>(pub &'a str); - impl Content for RepeatV<'_> { fn layout (&self, to: [u16;4]) -> [u16;4] { to @@ -101,7 +98,6 @@ impl Content for RepeatV<'_> { } pub struct RepeatH<'a>(pub &'a str); - impl Content for RepeatH<'_> { fn layout (&self, to: [u16;4]) -> [u16;4] { to diff --git a/tui/src/tui_output.rs b/tui/src/tui_output.rs index 83e65bc7..57cd5274 100644 --- a/tui/src/tui_output.rs +++ b/tui/src/tui_output.rs @@ -21,7 +21,7 @@ impl Output for TuiOut { } impl TuiOut { /// Spawn the output thread. - pub fn run_output + 'static> ( + pub fn run_output + Send + Sync + 'static> ( engine: &Arc>, state: &Arc>, timer: Duration