mod builtin;
use std::sync::mpsc::{Receiver, SyncSender};
use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle};
use renderer_core::{DrawCommand, RenderBackend, RendererError, RendererFactory};
use crate::config::RendererBackend;
use super::frame_thread::{FrameMsg, spawn_render_thread};
pub(super) use builtin::BuiltinHost;
pub trait SurfaceWindow:
platform_core::Window + Clone + Send + Sync + HasWindowHandle + HasDisplayHandle + 'static
{
}
impl<W> SurfaceWindow for W where
W: platform_core::Window + Clone + Send + Sync + HasWindowHandle + HasDisplayHandle + 'static
{
}
pub(super) type RawHandles<W> = fn(&W) -> (Option<RawWindowHandle>, Option<RawDisplayHandle>);
fn raw_handles_of<W: HasWindowHandle + HasDisplayHandle>(
window: &W,
) -> (Option<RawWindowHandle>, Option<RawDisplayHandle>) {
(
window.window_handle().ok().map(|h| h.as_raw()),
window.display_handle().ok().map(|h| h.as_raw()),
)
}
pub(super) struct SurfaceRenderer<W> {
pub(super) host: Box<dyn RendererHost<W>>,
pub(super) raw_handles: Option<RawHandles<W>>,
}
impl<W: SurfaceWindow> SurfaceRenderer<W> {
pub(super) fn builtin() -> Self {
Self {
host: Box::new(BuiltinHost::new()),
raw_handles: Some(raw_handles_of::<W>),
}
}
}
impl<W: 'static> SurfaceRenderer<W> {
pub(super) fn installed<F: RendererFactory<W>>(factory: F) -> Self {
Self {
host: Box::new(FactoryHost::new(factory)),
raw_handles: None,
}
}
}
pub(super) struct RenderChannels {
pub(super) tx: SyncSender<FrameMsg>,
pub(super) ret_rx: Receiver<Vec<DrawCommand>>,
}
pub(super) struct RendererRequest<'a> {
pub(super) backend: RendererBackend,
pub(super) transparent: bool,
pub(super) font_paths: &'a [std::path::PathBuf],
pub(super) font_data: &'a [Vec<u8>],
pub(super) paths: &'a dyn services_core::AppPathsProvider,
#[cfg_attr(not(feature = "hardware"), allow(dead_code))]
pub(super) app_name: &'a str,
}
pub(super) enum RendererStart {
Started {
keepalive: bool,
label: &'static str,
},
#[cfg_attr(not(feature = "hardware"), allow(dead_code))]
Building,
Failed(RendererError),
}
pub(super) trait RendererHost<W>: 'static {
fn start(&mut self, window: &W, req: &RendererRequest<'_>) -> RendererStart;
fn poll(&mut self) -> Option<RendererStart> {
None
}
fn is_building(&self) -> bool {
false
}
fn channels(&self) -> Option<&RenderChannels>;
fn suspend(&mut self);
fn retire(&mut self);
fn build_offscreen(
&mut self,
_window: &W,
_req: &RendererRequest<'_>,
) -> Option<Box<dyn RenderBackend>> {
None
}
}
pub(super) struct FactoryHost<W, F> {
factory: F,
channels: Option<RenderChannels>,
join: Option<std::thread::JoinHandle<Box<dyn RenderBackend + Send>>>,
_window: std::marker::PhantomData<W>,
}
impl<W, F> FactoryHost<W, F> {
pub(super) fn new(factory: F) -> Self {
Self {
factory,
channels: None,
join: None,
_window: std::marker::PhantomData,
}
}
}
impl<W, F> FactoryHost<W, F>
where
W: 'static,
F: RendererFactory<W>,
{
fn build(
&self,
window: &W,
req: &RendererRequest<'_>,
) -> Result<Box<dyn RenderBackend + Send>, RendererError> {
let fonts = super::font_config::build_font_config(
req.font_paths.to_vec(),
req.font_data.to_vec(),
&super::font_config::SystemFonts::from_provider(req.paths),
);
self.factory.build(
window,
renderer_core::RendererBuild {
fonts: &fonts,
transparent: req.transparent,
},
)
}
}
impl<W, F> RendererHost<W> for FactoryHost<W, F>
where
W: 'static,
F: RendererFactory<W>,
{
fn start(&mut self, window: &W, req: &RendererRequest<'_>) -> RendererStart {
let renderer = match self.build(window, req) {
Ok(renderer) => renderer,
Err(e) => return RendererStart::Failed(e),
};
let (tx, ret_rx, join) = spawn_render_thread(renderer);
self.channels = Some(RenderChannels { tx, ret_rx });
self.join = Some(join);
RendererStart::Started {
keepalive: false,
label: "installed renderer",
}
}
fn channels(&self) -> Option<&RenderChannels> {
self.channels.as_ref()
}
fn suspend(&mut self) {
self.retire();
}
fn retire(&mut self) {
self.channels = None;
if let Some(join) = self.join.take() {
let _ = join.join();
}
}
fn build_offscreen(
&mut self,
window: &W,
req: &RendererRequest<'_>,
) -> Option<Box<dyn RenderBackend>> {
match self.build(window, req) {
Ok(renderer) => Some(renderer),
Err(e) => {
tracing::error!("the installed renderer could not be built offscreen: {e}");
None
}
}
}
}