use std::path::PathBuf;
use std::sync::{mpsc, LazyLock};
use std::thread;
use tokio::sync::oneshot;
use crate::renderer::{Image, ImageRendererBuilder, RenderingError};
struct RenderRequest {
style_path: PathBuf,
z: u8,
x: u32,
y: u32,
response: oneshot::Sender<Result<Image, SingleThreadedRenderPoolError>>,
}
#[derive(Debug, Clone)]
pub struct SingleThreadedRenderPool {
rendering_requests: mpsc::Sender<RenderRequest>,
}
impl SingleThreadedRenderPool {
pub(crate) fn new() -> Self {
let (tx, rx) = mpsc::channel::<RenderRequest>();
thread::spawn(move || {
let mut renderer = ImageRendererBuilder::default().build_tile_renderer();
let mut current_style: Option<PathBuf> = None;
while let Ok(request) = rx.recv() {
if current_style.as_ref() != Some(&request.style_path) {
if let Err(e) = renderer.load_style_from_path(&request.style_path) {
let _ =
request.response.send(Err(SingleThreadedRenderPoolError::IOError(e)));
continue;
}
current_style = Some(request.style_path.clone());
}
let result = renderer
.render_tile(request.z, request.x, request.y)
.map_err(SingleThreadedRenderPoolError::RenderingError);
let _ = request.response.send(result);
}
});
Self { rendering_requests: tx }
}
pub async fn render_tile(
&self,
style_path: PathBuf,
z: u8,
x: u32,
y: u32,
) -> Result<Image, SingleThreadedRenderPoolError> {
let (response_tx, response_rx) = oneshot::channel();
self.rendering_requests
.send(RenderRequest { style_path, z, x, y, response: response_tx })
.map_err(|_| SingleThreadedRenderPoolError::FailedToSendRequest)?;
response_rx.await.map_err(|_| SingleThreadedRenderPoolError::FailedToReceiveResponse)?
}
#[must_use]
pub fn global_pool() -> &'static SingleThreadedRenderPool {
static GLOBAL_POOL: LazyLock<SingleThreadedRenderPool> =
LazyLock::new(SingleThreadedRenderPool::new);
&GLOBAL_POOL
}
}
#[derive(thiserror::Error, Debug)]
pub enum SingleThreadedRenderPoolError {
#[error(transparent)]
IOError(#[from] std::io::Error),
#[error(transparent)]
RenderingError(#[from] RenderingError),
#[error("Failed to send request to rendering thread")]
FailedToSendRequest,
#[error("Failed to receive response from rendering thread")]
FailedToReceiveResponse,
}