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