mirror of
https://github.com/Zenithsiz/zsw.git
synced 2026-02-07 11:59:19 +00:00
Merged update and render thread.
Updater not doesn't block anymore.
This commit is contained in:
parent
9ae9b3c73e
commit
8e97e2a259
41
src/app.rs
41
src/app.rs
@ -161,8 +161,7 @@ impl App {
|
||||
let _image_loaders =
|
||||
util::spawn_scoped_multiple(s, "Image loader", loader_threads, || || self.inner.image_loader.run())?;
|
||||
|
||||
// Spawn the updater and renderer thread
|
||||
let _updater_thread = util::spawn_scoped(s, "Updater", || Self::run_updater(&self.inner))?;
|
||||
// Spawn the renderer thread
|
||||
let _renderer_thread = util::spawn_scoped(s, "Renderer", || Self::run_renderer(&self.inner))?;
|
||||
|
||||
// Run event loop in this thread until we quit
|
||||
@ -225,14 +224,24 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs the updater
|
||||
fn run_updater(inner: &Inner) {
|
||||
/// Runs the renderer
|
||||
fn run_renderer(inner: &Inner) {
|
||||
// Duration we're sleep
|
||||
let sleep_duration = Duration::from_secs_f32(1.0 / 60.0);
|
||||
|
||||
loop {
|
||||
// Render
|
||||
// Update
|
||||
// Note: The update is only useful for displaying, so there's no use
|
||||
// in running it in another thread.
|
||||
// Especially given that `update` doesn't block.
|
||||
let (res, frame_duration) = crate::util::measure(|| Self::update(inner));
|
||||
match res {
|
||||
Ok(()) => log::trace!("Took {frame_duration:?} to update"),
|
||||
Err(err) => log::warn!("Unable to update: {err:?}"),
|
||||
};
|
||||
|
||||
// Render
|
||||
let (res, frame_duration) = crate::util::measure(|| Self::render(inner));
|
||||
match res {
|
||||
Ok(()) => log::trace!("Took {frame_duration:?} to render"),
|
||||
Err(err) => log::warn!("Unable to render: {err:?}"),
|
||||
@ -245,7 +254,7 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates
|
||||
/// Updates all panels
|
||||
fn update(inner: &Inner) -> Result<(), anyhow::Error> {
|
||||
let mut panels = inner.panels.lock();
|
||||
for panel in &mut *panels {
|
||||
@ -263,26 +272,6 @@ impl App {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Runs the renderer
|
||||
fn run_renderer(inner: &Inner) {
|
||||
// Duration we're sleep
|
||||
let sleep_duration = Duration::from_secs_f32(1.0 / 60.0);
|
||||
|
||||
loop {
|
||||
// Render
|
||||
let (res, frame_duration) = crate::util::measure(|| Self::render(inner));
|
||||
match res {
|
||||
Ok(()) => log::trace!("Took {frame_duration:?} to render"),
|
||||
Err(err) => log::warn!("Unable to render: {err:?}"),
|
||||
};
|
||||
|
||||
// Then sleep until next frame
|
||||
if let Some(duration) = sleep_duration.checked_sub(frame_duration) {
|
||||
thread::sleep(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders
|
||||
fn render(inner: &Inner) -> Result<(), anyhow::Error> {
|
||||
// Draw egui
|
||||
|
||||
251
src/panel.rs
251
src/panel.rs
@ -15,10 +15,16 @@ pub use self::{
|
||||
};
|
||||
|
||||
// Imports
|
||||
use crate::{img::ImageLoader, Rect};
|
||||
use crate::{
|
||||
img::{Image, ImageLoader},
|
||||
Rect,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use cgmath::{Matrix4, Vector3};
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{
|
||||
mem,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
/// Panel
|
||||
@ -46,6 +52,58 @@ pub struct Panel {
|
||||
/// Fade point
|
||||
// TODO: Ensure it's between 0.5 and 1.0
|
||||
pub fade_point: f32,
|
||||
|
||||
/// Next image state
|
||||
pub next_image_state: NextImageState,
|
||||
}
|
||||
|
||||
/// Next image
|
||||
#[derive(Debug)]
|
||||
pub enum NextImageState {
|
||||
/// Ready
|
||||
Ready(Image),
|
||||
|
||||
/// Waiting
|
||||
Waiting {
|
||||
/// Instant we've been waiting since
|
||||
since: Instant,
|
||||
},
|
||||
|
||||
/// Empty
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl NextImageState {
|
||||
/// Loads the next image, if waiting or empty
|
||||
pub fn load_next(&mut self, image_loader: &ImageLoader) -> Result<(), anyhow::Error> {
|
||||
*self = match mem::replace(self, Self::Empty) {
|
||||
Self::Ready(image) => Self::Ready(image),
|
||||
Self::Waiting { since } => match image_loader.try_recv().context("Unable to receive next image")? {
|
||||
Some(image) => {
|
||||
log::debug!("Received image after waiting for {:?}", since.elapsed());
|
||||
Self::Ready(image)
|
||||
},
|
||||
None => Self::Waiting { since },
|
||||
},
|
||||
Self::Empty => match image_loader.try_recv().context("Unable to receive next image")? {
|
||||
Some(image) => Self::Ready(image),
|
||||
None => Self::Waiting { since: Instant::now() },
|
||||
},
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Takes the image, if any
|
||||
pub fn take_image(&mut self) -> Option<Image> {
|
||||
let image;
|
||||
(*self, image) = match mem::replace(self, Self::Empty) {
|
||||
Self::Ready(image) => (Self::Empty, Some(image)),
|
||||
Self::Waiting { since } => (Self::Waiting { since }, None),
|
||||
Self::Empty => (Self::Waiting { since: Instant::now() }, None),
|
||||
};
|
||||
image
|
||||
}
|
||||
}
|
||||
|
||||
impl Panel {
|
||||
@ -57,97 +115,92 @@ impl Panel {
|
||||
progress: 0.0,
|
||||
image_duration,
|
||||
fade_point,
|
||||
next_image_state: NextImageState::Empty,
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates this panel
|
||||
// TODO: Not block if the image isn't ready.
|
||||
pub fn update(
|
||||
&mut self, device: &wgpu::Device, queue: &wgpu::Queue, uniforms_bind_group_layout: &wgpu::BindGroupLayout,
|
||||
texture_bind_group_layout: &wgpu::BindGroupLayout, image_loader: &ImageLoader,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
// If we've been waiting for an image, try to load it
|
||||
self.next_image_state
|
||||
.load_next(image_loader)
|
||||
.context("Unable to load next image")?;
|
||||
|
||||
// Next frame's progress
|
||||
let next_progress = self.progress + (1.0 / 60.0) / self.image_duration.as_secs_f32();
|
||||
|
||||
// Progress on image swap
|
||||
let swapped_progress = self.progress - self.fade_point;
|
||||
|
||||
// If past the fade point
|
||||
let past_fade = self.progress >= self.fade_point;
|
||||
|
||||
// If we finished the current image
|
||||
let finished = self.progress >= 1.0;
|
||||
|
||||
// Check the image state
|
||||
(self.state, self.progress) = match std::mem::replace(&mut self.state, PanelState::Empty) {
|
||||
// If we're empty, get the next image
|
||||
PanelState::Empty => {
|
||||
let image = PanelImage::new(
|
||||
device,
|
||||
queue,
|
||||
uniforms_bind_group_layout,
|
||||
texture_bind_group_layout,
|
||||
image_loader,
|
||||
)
|
||||
.context("Unable to create image")?;
|
||||
(PanelState::PrimaryOnly { image }, 0.0)
|
||||
// Update the image state
|
||||
// Note: We have to replace the state with `Empty` temporarily due to
|
||||
// panics that might occur while updating.
|
||||
(self.state, self.progress) = match mem::replace(&mut self.state, PanelState::Empty) {
|
||||
// If we're empty, try to load the next image
|
||||
PanelState::Empty => match self.next_image_state.take_image() {
|
||||
Some(image) => (
|
||||
PanelState::PrimaryOnly {
|
||||
front: PanelImage::new(
|
||||
device,
|
||||
queue,
|
||||
uniforms_bind_group_layout,
|
||||
texture_bind_group_layout,
|
||||
&image,
|
||||
)
|
||||
.context("Unable to create panel image")?,
|
||||
},
|
||||
0.0,
|
||||
),
|
||||
None => (PanelState::Empty, 0.0),
|
||||
},
|
||||
|
||||
// If we only have the primary and we're past the fade point, get the next image
|
||||
// Note: We do this so we can render the first image without waiting
|
||||
// for both images to load
|
||||
// TODO: Redo this setup
|
||||
PanelState::PrimaryOnly { image: front } if past_fade => {
|
||||
let back = PanelImage::new(
|
||||
device,
|
||||
queue,
|
||||
uniforms_bind_group_layout,
|
||||
texture_bind_group_layout,
|
||||
image_loader,
|
||||
)
|
||||
.context("Unable to create image")?;
|
||||
|
||||
(PanelState::Both { front, back }, next_progress)
|
||||
// If we only have the primary, try to load the next image
|
||||
PanelState::PrimaryOnly { front } => match self.next_image_state.take_image() {
|
||||
Some(image) => (
|
||||
PanelState::Both {
|
||||
front,
|
||||
back: PanelImage::new(
|
||||
device,
|
||||
queue,
|
||||
uniforms_bind_group_layout,
|
||||
texture_bind_group_layout,
|
||||
&image,
|
||||
)
|
||||
.context("Unable to create panel image")?,
|
||||
},
|
||||
next_progress,
|
||||
),
|
||||
None => (PanelState::PrimaryOnly { front }, next_progress),
|
||||
},
|
||||
|
||||
// If we have both, update the progress and swap them if finished
|
||||
PanelState::Both { front, back } if finished => {
|
||||
// Note: Front and back are swapped here since we implicitly swap
|
||||
match self::update_swapped(
|
||||
front,
|
||||
back,
|
||||
None,
|
||||
image_loader,
|
||||
device,
|
||||
queue,
|
||||
texture_bind_group_layout,
|
||||
past_fade,
|
||||
)
|
||||
.context("Unable to update swapped image")?
|
||||
{
|
||||
(true, state) => (state, swapped_progress),
|
||||
(false, state) => (state, next_progress),
|
||||
}
|
||||
// If we have both, try to update the progress and swap them if finished
|
||||
PanelState::Both { mut front, back } if finished => match self.next_image_state.take_image() {
|
||||
// Note: We update the front and swap them
|
||||
Some(image) => {
|
||||
front
|
||||
.update(device, queue, texture_bind_group_layout, &image)
|
||||
.context("Unable to update texture")?;
|
||||
(
|
||||
PanelState::Both {
|
||||
front: back,
|
||||
back: front,
|
||||
},
|
||||
swapped_progress,
|
||||
)
|
||||
},
|
||||
// Note: If we're done without a next image, then just stay at 1.0
|
||||
None => (PanelState::Both { front, back }, 1.0),
|
||||
},
|
||||
|
||||
// If we're swapped, try to update
|
||||
PanelState::Swapped { front, back, since } => match self::update_swapped(
|
||||
back,
|
||||
front,
|
||||
Some(since),
|
||||
image_loader,
|
||||
device,
|
||||
queue,
|
||||
texture_bind_group_layout,
|
||||
past_fade,
|
||||
)
|
||||
.context("Unable to update swapped image")?
|
||||
{
|
||||
(true, state) => (state, swapped_progress),
|
||||
(false, state) => (state, next_progress),
|
||||
},
|
||||
|
||||
// Else keep the current state and advance
|
||||
state => (state, next_progress),
|
||||
// Else just update the progress
|
||||
state @ PanelState::Both { .. } => (state, next_progress),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
@ -182,9 +235,7 @@ impl Panel {
|
||||
// Get the images to render
|
||||
let (front, back) = match &mut self.state {
|
||||
PanelState::Empty => (None, None),
|
||||
PanelState::PrimaryOnly { image, .. } | PanelState::Swapped { front: image, .. } => {
|
||||
(Some((image, 1.0, self.progress)), None)
|
||||
},
|
||||
PanelState::PrimaryOnly { front, .. } => (Some((front, 1.0, self.progress)), None),
|
||||
PanelState::Both { front, back } => (
|
||||
Some((front, 1.0 - back_alpha, self.progress)),
|
||||
Some((back, back_alpha, back_progress)),
|
||||
@ -230,7 +281,7 @@ pub enum PanelState {
|
||||
/// The primary image is loaded. The back image is still not available
|
||||
PrimaryOnly {
|
||||
/// Image
|
||||
image: PanelImage,
|
||||
front: PanelImage,
|
||||
},
|
||||
|
||||
/// Both
|
||||
@ -243,56 +294,4 @@ pub enum PanelState {
|
||||
/// Back image
|
||||
back: PanelImage,
|
||||
},
|
||||
|
||||
/// Swapped
|
||||
///
|
||||
/// Front and back images have been swapped, and the next image needs
|
||||
/// to be loaded
|
||||
Swapped {
|
||||
/// Front image
|
||||
front: PanelImage,
|
||||
|
||||
/// Back image that needs to be swapped
|
||||
back: PanelImage,
|
||||
|
||||
/// Instant we were swapped
|
||||
since: Instant,
|
||||
},
|
||||
}
|
||||
|
||||
/// Updates a swapped image state and returns the next state
|
||||
#[allow(clippy::too_many_arguments)] // TODO:
|
||||
fn update_swapped(
|
||||
mut back: PanelImage, front: PanelImage, mut since: Option<Instant>, image_loader: &ImageLoader,
|
||||
device: &wgpu::Device, queue: &wgpu::Queue, texture_bind_group_layout: &wgpu::BindGroupLayout, force_wait: bool,
|
||||
) -> Result<(bool, PanelState), anyhow::Error> {
|
||||
// If we're force waiting and don't have a `since`, create it,
|
||||
// so we can keep track of how long the request took
|
||||
if force_wait && since.is_none() {
|
||||
since = Some(Instant::now());
|
||||
}
|
||||
|
||||
let swapped = back
|
||||
.try_update(image_loader, device, queue, texture_bind_group_layout, force_wait)
|
||||
.context("Unable to get next image")?;
|
||||
let state = match swapped {
|
||||
// If we updated, switch to `Both`
|
||||
true => {
|
||||
// If we didn't just update it, log how long it took
|
||||
if let Some(since) = since {
|
||||
let duration = Instant::now().saturating_duration_since(since);
|
||||
log::trace!("Waited {duration:?} for the next image");
|
||||
}
|
||||
PanelState::Both { front, back }
|
||||
},
|
||||
|
||||
// Else stay in `Swapped`
|
||||
false => PanelState::Swapped {
|
||||
back,
|
||||
front,
|
||||
since: since.unwrap_or_else(Instant::now),
|
||||
},
|
||||
};
|
||||
|
||||
Ok((swapped, state))
|
||||
}
|
||||
|
||||
@ -2,8 +2,7 @@
|
||||
|
||||
// Imports
|
||||
use super::PanelUniforms;
|
||||
use crate::img::{Image, ImageLoader, ImageUvs};
|
||||
use anyhow::Context;
|
||||
use crate::img::{Image, ImageUvs};
|
||||
use cgmath::Vector2;
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
@ -38,23 +37,15 @@ pub struct PanelImage {
|
||||
|
||||
impl PanelImage {
|
||||
/// Creates a new image
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns error if unable to create the gl texture or the vertex buffer
|
||||
pub fn new(
|
||||
device: &wgpu::Device, queue: &wgpu::Queue, uniforms_bind_group_layout: &wgpu::BindGroupLayout,
|
||||
texture_bind_group_layout: &wgpu::BindGroupLayout, image_loader: &ImageLoader,
|
||||
texture_bind_group_layout: &wgpu::BindGroupLayout, image: &Image,
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
// Get an image receiver and the initial image
|
||||
let image = image_loader.recv().context("Unable to get image")?;
|
||||
let image_size = Vector2::new(image.width(), image.height());
|
||||
|
||||
// Create the texture and sampler
|
||||
let (texture, texture_view) = self::create_image_texture(&image, device, queue);
|
||||
let texture_sampler = create_texture_sampler(device);
|
||||
|
||||
let swap_dir = rand::random();
|
||||
let (texture, texture_view) = self::create_image_texture(image, device, queue);
|
||||
let texture_sampler = self::create_texture_sampler(device);
|
||||
|
||||
// Create the uniforms
|
||||
let uniforms = PanelUniforms::new();
|
||||
let uniforms_descriptor = wgpu::util::BufferInitDescriptor {
|
||||
label: None,
|
||||
@ -85,29 +76,23 @@ impl PanelImage {
|
||||
texture_bind_group,
|
||||
uniforms,
|
||||
uniforms_bind_group,
|
||||
image_size,
|
||||
swap_dir,
|
||||
image_size: Vector2::new(image.width(), image.height()),
|
||||
swap_dir: rand::random(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Tries to update this image and returns if actually updated
|
||||
/// Updates this image
|
||||
#[allow(clippy::unnecessary_wraps)] // It might fail in the future
|
||||
pub fn try_update(
|
||||
&mut self, image_loader: &ImageLoader, device: &wgpu::Device, queue: &wgpu::Queue,
|
||||
texture_bind_group_layout: &wgpu::BindGroupLayout, force_wait: bool,
|
||||
) -> Result<bool, anyhow::Error> {
|
||||
let image = match image_loader.try_recv().context("Unable to receive image")? {
|
||||
Some(image) => image,
|
||||
None => match force_wait {
|
||||
true => image_loader.recv().context("Unable to receive image")?,
|
||||
false => return Ok(false),
|
||||
},
|
||||
};
|
||||
pub fn update(
|
||||
&mut self, device: &wgpu::Device, queue: &wgpu::Queue, texture_bind_group_layout: &wgpu::BindGroupLayout,
|
||||
image: &Image,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
// Update the image
|
||||
self.image_size = Vector2::new(image.width(), image.height());
|
||||
self.swap_dir = rand::random();
|
||||
|
||||
// Then update our texture
|
||||
(self.texture, self.texture_view) = self::create_image_texture(&image, device, queue);
|
||||
(self.texture, self.texture_view) = self::create_image_texture(image, device, queue);
|
||||
self.texture_bind_group = self::create_texture_bind_group(
|
||||
texture_bind_group_layout,
|
||||
&self.texture_view,
|
||||
@ -115,7 +100,7 @@ impl PanelImage {
|
||||
device,
|
||||
);
|
||||
|
||||
Ok(true)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns this image's uvs for a panel size
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user