pebble/rendering/
graphics_plugin.rs1use crate::{
2 prelude::{Backend, Commands, GPUSurfaceHandle, Plugin, PresentableWindow, Res, ResMut, SystemStage, WindowResource},
3 rendering::sync::init_channel,
4};
5
6pub struct GraphicsPlugin<B, W> {
25 _marker: std::marker::PhantomData<(B, W)>,
26}
27
28impl<B: Backend, W: PresentableWindow> GraphicsPlugin<B, W>
29where
30 W::Handle: GPUSurfaceHandle,
31{
32 pub fn new() -> Self {
33 Self {
34 _marker: std::marker::PhantomData,
35 }
36 }
37}
38
39impl<B: Backend, W: PresentableWindow> Plugin for GraphicsPlugin<B, W>
40where
41 W::Handle: GPUSurfaceHandle,
42{
43 #[cfg(not(target_arch = "wasm32"))]
44 fn build(&self, app: &mut crate::prelude::App) {
45 let (handle, w, h) = {
46 let window = app.get_resource::<WindowResource<W>>();
47 let (w, h) = W::size(&window.handle);
48 (window.handle.clone(), w, h)
49 };
50
51 let (sender, receiver) = init_channel::<B>();
52 B::init(handle, w, h, sender);
53 match receiver.recv() {
54 Ok(backend) => {
55 app.add_resource(backend);
56 }
57 Err(_) => {
58 tracing::error!(
59 "GPU backend init sender was dropped without ever sending a value — the app \
60 has no usable backend; every backend-dependent system will panic on its first \
61 Res<B>/ResMut<B> fetch"
62 );
63 }
64 }
65
66 app.add_system(SystemStage::PreRender, handle_resize_async::<B, W>);
67 }
68
69 #[cfg(target_arch = "wasm32")]
75 fn build(&self, app: &mut crate::prelude::App) {
76 let (handle, w, h) = {
77 let window = app.get_resource::<WindowResource<W>>();
78 let (w, h) = W::size(&window.handle);
79 (window.handle.clone(), w, h)
80 };
81
82 let (sender, receiver) = init_channel::<B>();
83 B::init(handle, w, h, sender);
84
85 let mut receiver = Some(receiver);
86 app.set_ready_gate(move |world, resources| {
87 let Some(recv) = receiver.as_mut() else { return false };
88 match recv.try_recv() {
89 Ok(backend) => {
90 resources.insert_resource(world, backend);
91 receiver = None;
92 true
93 }
94 Err(oneshot::TryRecvError::Empty) => false,
95 Err(oneshot::TryRecvError::Disconnected) => {
96 tracing::error!(
97 "GPU backend init sender was dropped without ever sending a value — the \
98 app has no usable backend and will stay idle forever"
99 );
100 receiver = None;
101 false
102 }
103 }
104 });
105
106 app.add_system(SystemStage::PreRender, handle_resize_async::<B, W>);
107 }
108}
109
110struct LastWindowSize(u32, u32);
111
112fn handle_resize_async<B: Backend, W: PresentableWindow>(
119 mut commands: Commands,
120 backend: Option<ResMut<B>>,
121 window: Res<WindowResource<W>>,
122 last_size: Option<Res<LastWindowSize>>,
123) where
124 W::Handle: GPUSurfaceHandle,
125{
126 let Some(mut backend) = backend else { return };
127 let (w, h) = W::size(&window.handle);
128 if w == 0 || h == 0 {
129 return;
130 }
131
132 if let Some(last_size) = &last_size {
133 if last_size.0 == w && last_size.1 == h {
134 return;
135 }
136 }
137
138 backend.resize(w, h);
139 commands.insert_resource(LastWindowSize(w, h));
140}