use crate::{
prelude::{Backend, Commands, GPUSurfaceHandle, Plugin, PresentableWindow, Res, ResMut, SystemStage, WindowResource},
rendering::sync::init_channel,
};
pub struct GraphicsPlugin<B, W> {
_marker: std::marker::PhantomData<(B, W)>,
}
impl<B: Backend, W: PresentableWindow> GraphicsPlugin<B, W>
where
W::Handle: GPUSurfaceHandle,
{
pub fn new() -> Self {
Self {
_marker: std::marker::PhantomData,
}
}
}
impl<B: Backend, W: PresentableWindow> Plugin for GraphicsPlugin<B, W>
where
W::Handle: GPUSurfaceHandle,
{
#[cfg(not(target_arch = "wasm32"))]
fn build(&self, app: &mut crate::prelude::App) {
let (handle, w, h) = {
let window = app.get_resource::<WindowResource<W>>();
let (w, h) = W::size(&window.handle);
(window.handle.clone(), w, h)
};
let (sender, receiver) = init_channel::<B>();
B::init(handle, w, h, sender);
match receiver.recv() {
Ok(backend) => {
app.add_resource(backend);
}
Err(_) => {
tracing::error!(
"GPU backend init sender was dropped without ever sending a value — the app \
has no usable backend; every backend-dependent system will panic on its first \
Res<B>/ResMut<B> fetch"
);
}
}
app.add_system(SystemStage::PreRender, handle_resize_async::<B, W>);
}
#[cfg(target_arch = "wasm32")]
fn build(&self, app: &mut crate::prelude::App) {
let (handle, w, h) = {
let window = app.get_resource::<WindowResource<W>>();
let (w, h) = W::size(&window.handle);
(window.handle.clone(), w, h)
};
let (sender, receiver) = init_channel::<B>();
B::init(handle, w, h, sender);
let mut receiver = Some(receiver);
app.set_ready_gate(move |world, resources| {
let Some(recv) = receiver.as_mut() else { return false };
match recv.try_recv() {
Ok(backend) => {
resources.insert_resource(world, backend);
receiver = None;
true
}
Err(oneshot::TryRecvError::Empty) => false,
Err(oneshot::TryRecvError::Disconnected) => {
tracing::error!(
"GPU backend init sender was dropped without ever sending a value — the \
app has no usable backend and will stay idle forever"
);
receiver = None;
false
}
}
});
app.add_system(SystemStage::PreRender, handle_resize_async::<B, W>);
}
}
struct LastWindowSize(u32, u32);
fn handle_resize_async<B: Backend, W: PresentableWindow>(
mut commands: Commands,
backend: Option<ResMut<B>>,
window: Res<WindowResource<W>>,
last_size: Option<Res<LastWindowSize>>,
) where
W::Handle: GPUSurfaceHandle,
{
let Some(mut backend) = backend else { return };
let (w, h) = W::size(&window.handle);
if w == 0 || h == 0 {
return;
}
if let Some(last_size) = &last_size {
if last_size.0 == w && last_size.1 == h {
return;
}
}
backend.resize(w, h);
commands.insert_resource(LastWindowSize(w, h));
}