pebble/rendering/
graphics_plugin.rs1use crate::{
2 prelude::{
3 Backend, Commands, GPUSurfaceHandle, Plugin, PresentableWindow, Res, ResMut, SystemStage,
4 WindowResource,
5 },
6 rendering::{async_init::PendingBackend, sync::init_channel},
7};
8
9pub struct GraphicsPlugin<B, W> {
17 _marker: std::marker::PhantomData<(B, W)>,
18}
19
20impl<B: Backend, W: PresentableWindow> GraphicsPlugin<B, W>
21where
22 W::Handle: GPUSurfaceHandle,
23{
24 pub fn new() -> Self {
25 Self {
26 _marker: std::marker::PhantomData,
27 }
28 }
29}
30
31impl<B: Backend, W: PresentableWindow> Plugin for GraphicsPlugin<B, W>
32where
33 W::Handle: GPUSurfaceHandle,
34{
35 fn build(&self, app: &mut crate::prelude::App) {
36 app.add_system(SystemStage::Startup, setup_gpu_async::<B, W>)
37 .add_system(SystemStage::PreRender, poll_backend_ready::<B>)
38 .add_system(SystemStage::PreRender, handle_resize_async::<B, W>);
39 }
40}
41
42struct LastWindowSize(u32, u32);
43
44fn setup_gpu_async<B: Backend, W>(mut commands: Commands, window: Res<WindowResource<W>>) -> Option<()>
49where
50 W: PresentableWindow,
51 W::Handle: GPUSurfaceHandle,
52{
53 let (w, h) = W::size(&window.handle);
54 let (sender, receiver) = init_channel::<B>();
55 B::init(window.handle.clone(), w, h, sender);
56 commands.insert_resource(PendingBackend::<B> {
57 receiver: std::sync::Mutex::new(receiver),
58 });
59 Some(())
60}
61
62fn poll_backend_ready<B: Backend>(mut commands: Commands, pending: Option<Res<PendingBackend<B>>>) {
65 if let Some(p) = pending {
66 let mut guard = match p.receiver.lock() {
67 Ok(g) => g,
68 Err(poisoned) => poisoned.into_inner(),
69 };
70
71 if let Ok(backend) = guard.try_recv() {
72 commands.insert_resource(backend);
73 commands.remove_resource::<PendingBackend<B>>();
74 }
75 }
76}
77
78fn handle_resize_async<B: Backend, W: PresentableWindow>(
85 mut commands: Commands,
86 backend: Option<ResMut<B>>,
87 window: Res<WindowResource<W>>,
88 last_size: Option<Res<LastWindowSize>>,
89) where
90 W::Handle: GPUSurfaceHandle,
91{
92 let Some(mut backend) = backend else { return };
93 let (w, h) = W::size(&window.handle);
94 if w == 0 || h == 0 {
95 return;
96 }
97
98 if let Some(last_size) = &last_size {
99 if last_size.0 == w && last_size.1 == h {
100 return;
101 }
102 }
103
104 backend.resize(w, h);
105 commands.insert_resource(LastWindowSize(w, h));
106}