Skip to main content

pebble/rendering/
web_backend.rs

1use crate::{
2    prelude::{
3        Backend, Commands, GPUSurfaceHandle, Plugin, PresentableWindow, Res, ResMut, SystemStage,
4        WindowResource,
5    },
6    rendering::sync::{InitReceiver, InitSender, init_channel},
7};
8
9pub trait AsyncInit: Backend {
10    fn init_async(handle: impl GPUSurfaceHandle, width: u32, height: u32, sender: InitSender<Self>);
11}
12
13pub struct PendingBackend<B: AsyncInit> {
14    receiver: std::sync::Mutex<InitReceiver<B>>,
15}
16
17pub struct AsyncGraphicsPlugin<B, W>(std::marker::PhantomData<(B, W)>);
18
19impl<B, W> AsyncGraphicsPlugin<B, W> {
20    pub fn new() -> Self {
21        Self(std::marker::PhantomData)
22    }
23}
24
25impl<B: AsyncInit, W> Plugin for AsyncGraphicsPlugin<B, W>
26where
27    W: PresentableWindow,
28    W::Handle: GPUSurfaceHandle,
29{
30    fn build(&self, app: &mut crate::app::App) {
31        app.add_system(SystemStage::Startup, setup_gpu_async::<B, W>)
32            .add_system(SystemStage::PreRender, poll_backend_ready::<B>)
33            .add_system(SystemStage::PreRender, handle_resize_async::<B, W>);
34    }
35}
36
37fn setup_gpu_async<B: AsyncInit, W>(mut commands: Commands, window: Res<WindowResource<W>>)
38where
39    W: PresentableWindow,
40    W::Handle: GPUSurfaceHandle,
41{
42    let (w, h) = W::size(&window.handle);
43    let (sender, receiver) = init_channel::<B>();
44    B::init_async(window.handle.clone(), w, h, sender);
45    commands.insert_resource(PendingBackend::<B> {
46        receiver: std::sync::Mutex::new(receiver),
47    });
48}
49
50fn poll_backend_ready<B: AsyncInit>(
51    mut commands: Commands,
52    pending: Option<Res<PendingBackend<B>>>,
53) {
54    if let Some(p) = pending {
55        let mut guard = match p.receiver.lock() {
56            Ok(g) => g,
57            Err(poisoned) => poisoned.into_inner(),
58        };
59
60        if let Ok(backend) = guard.try_recv() {
61            commands.insert_resource(backend);
62            commands.remove_resource::<PendingBackend<B>>();
63        }
64    }
65}
66
67fn handle_resize_async<B: AsyncInit, W: PresentableWindow>(
68    backend: Option<ResMut<B>>,
69    window: Res<WindowResource<W>>,
70) where
71    W::Handle: GPUSurfaceHandle,
72{
73    let Some(mut backend) = backend else { return };
74    let (w, h) = W::size(&window.handle);
75    if w > 0 && h > 0 {
76        backend.resize(w, h);
77    }
78}