use std::sync::{ mpsc::{channel, Receiver}, Arc, }; use notify::{ event::{CreateKind, ModifyKind}, EventKind, RecommendedWatcher, Watcher, }; use pollster::FutureExt; use primitive::{RoundedRect, UIPos}; use shape::ShapePipeline; use wgpu::util::StagingBelt; use winit::{dpi::PhysicalSize, window::Window}; pub mod primitive; mod shape; pub const CLEAR_COLOR: wgpu::Color = wgpu::Color::BLACK; pub struct Renderer { surface: wgpu::Surface<'static>, device: wgpu::Device, queue: wgpu::Queue, config: wgpu::SurfaceConfiguration, adapter: wgpu::Adapter, encoder: wgpu::CommandEncoder, staging_belt: StagingBelt, shape_pipeline: ShapePipeline, recv: Receiver>, } impl Renderer { pub fn new(window: Arc) -> Self { let size = window.inner_size(); let (send, recv) = channel(); let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("data/test.rhai"); let w2 = window.clone(); std::thread::spawn(move || { let (tx, rx) = channel(); let mut watcher = RecommendedWatcher::new(tx, Default::default()).unwrap(); watcher .watch("data".as_ref(), notify::RecursiveMode::Recursive) .unwrap(); for res in rx { let Ok(ev) = res else { continue; }; if !ev.paths.contains(&path) { continue; } if !matches!( ev.kind, EventKind::Create(CreateKind::File) | EventKind::Modify(ModifyKind::Data(_)) ) { continue; } println!("reloaded {ev:?}"); let mut engine = rhai::Engine::new(); engine.build_type::(); engine.register_type::(); engine.register_fn("anchor_offset", UIPos::anchor_offset); engine.register_fn("rect", RoundedRect::default); let Ok(code) = std::fs::read_to_string(&path) else { continue; }; let rects = engine .eval::(&code) .map_err(|e| println!("{e:?}")) .unwrap_or_default() .into_iter() .filter_map(|d| match rhai::Dynamic::try_cast(d.clone()) { Some(v) => Some(v), None => { println!("{d:?} is not a RoundedRect"); None } }) .collect::>(); send.send(rects).unwrap(); w2.request_redraw(); } }); let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { backends: wgpu::Backends::PRIMARY, ..Default::default() }); let surface = instance .create_surface(window.clone()) .expect("Could not create window surface!"); let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::default(), compatible_surface: Some(&surface), force_fallback_adapter: false, }) .block_on() .expect("Could not get adapter!"); let (device, queue) = adapter .request_device(&wgpu::DeviceDescriptor { required_features: wgpu::Features::empty(), required_limits: wgpu::Limits::default(), ..Default::default() }) .block_on() .expect("Could not get device!"); let surface_caps = surface.get_capabilities(&adapter); let surface_format = surface_caps .formats .iter() .copied() .find(|f| f.is_srgb()) .unwrap_or(surface_caps.formats[0]); let config = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: surface_format, width: size.width, height: size.height, present_mode: surface_caps.present_modes[0], alpha_mode: surface_caps.alpha_modes[0], desired_maximum_frame_latency: 2, view_formats: vec![], }; surface.configure(&device, &config); let staging_belt = StagingBelt::new(4096 * 4); let encoder = Self::create_encoder(&device); let shape_pipeline = ShapePipeline::new(&device, &config); Self { surface, device, queue, config, adapter, encoder, staging_belt, shape_pipeline, recv, } } fn create_encoder(device: &wgpu::Device) -> wgpu::CommandEncoder { device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Render Encoder"), }) } pub fn draw(&mut self) { if let Some(rects) = self.recv.try_iter().last() { self.shape_pipeline .update(&self.device, &self.queue, &rects); } let output = self.surface.get_current_texture().unwrap(); let view = output .texture .create_view(&wgpu::TextureViewDescriptor::default()); let mut encoder = std::mem::replace(&mut self.encoder, Self::create_encoder(&self.device)); { let render_pass = &mut encoder.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(CLEAR_COLOR), store: wgpu::StoreOp::Store, }, })], ..Default::default() }); self.shape_pipeline.draw(render_pass); } self.queue.submit(std::iter::once(encoder.finish())); self.staging_belt.finish(); output.present(); self.staging_belt.recall(); } pub fn resize(&mut self, size: &PhysicalSize) { self.config.width = size.width; self.config.height = size.height; self.surface.configure(&self.device, &self.config); self.shape_pipeline.resize(size, &self.queue); } }