use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop, EventLoopProxy},
window::{Window, WindowBuilder},
};
use crate::loader::Loader;
use crate::prelude::*;
pub struct Sandbox<M: 'static + Model, L: Loader> {
pub ui: crate::backend::wgpu::Ui<M, EventLoopProxy<Command<M::Message>>, L>,
event_loop: Option<EventLoop<Command<M::Message>>>,
surface: wgpu::Surface,
#[allow(unused)]
adapter: wgpu::Adapter,
device: wgpu::Device,
queue: wgpu::Queue,
swap_chain: wgpu::SwapChain,
sc_desc: wgpu::SwapChainDescriptor,
window: Window,
}
impl<T: 'static + Model, L: Loader> Sandbox<T, L> {
pub async fn new(model: T, loader: L, builder: WindowBuilder) -> Self {
let event_loop = EventLoop::<Command<T::Message>>::with_user_event();
let window = builder.build(&event_loop).unwrap();
let size = window.inner_size();
let swapchain_format = wgpu::TextureFormat::Bgra8Unorm;
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(&window) };
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface),
})
.await
.expect("Failed to find an appropriate adapter");
let trace_dir = std::env::var("WGPU_TRACE");
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
features: Default::default(),
limits: wgpu::Limits::default(),
shader_validation: false,
},
trace_dir.ok().as_ref().map(std::path::Path::new),
)
.await
.expect("Failed retrieve device and queue");
let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: swapchain_format,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Mailbox,
};
let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let ui = crate::backend::wgpu::Ui::new(
model,
event_loop.create_proxy(),
loader,
Rectangle::from_wh(size.width as f32, size.height as f32),
swapchain_format,
&device,
);
Sandbox {
ui,
event_loop: Some(event_loop),
surface,
adapter,
device,
queue,
swap_chain,
sc_desc,
window,
}
}
pub async fn run(mut self) {
let event_loop = self.event_loop.take().unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::UserEvent(command) => {
self.ui.command(command);
}
Event::WindowEvent {
event: WindowEvent::Resized(size),
..
} => {
self.sc_desc.width = size.width;
self.sc_desc.height = size.height;
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
self.ui
.resize(Rectangle::from_wh(size.width as f32, size.height as f32));
}
Event::RedrawRequested(_) => {
let frame = self
.swap_chain
.get_current_frame()
.expect("Failed to acquire next swap chain texture");
let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.output.view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: true,
},
}],
depth_stencil_attachment: None,
});
self.ui.draw(&self.device, &self.queue, &mut pass);
}
self.queue.submit(Some(encoder.finish()));
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
other => {
if let Some(event) = crate::backend::winit::convert_event(other) {
self.ui.event(event);
}
}
}
if self.ui.needs_redraw() {
self.window.request_redraw();
}
});
}
}