Initial attempt to implement Render Graph
This commit is contained in:
		
							parent
							
								
									173cfe2acf
								
							
						
					
					
						commit
						3732fc5e3d
					
				
					 33 changed files with 8851 additions and 938 deletions
				
			
		
							
								
								
									
										2159
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										2159
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -9,6 +9,7 @@ edition = "2021"
 | 
				
			||||||
members = [
 | 
					members = [
 | 
				
			||||||
  "khors-core",
 | 
					  "khors-core",
 | 
				
			||||||
  "vendor/egui-vulkano",
 | 
					  "vendor/egui-vulkano",
 | 
				
			||||||
 | 
					  "vendor/egui-snarl",
 | 
				
			||||||
  "modules/khors-app",
 | 
					  "modules/khors-app",
 | 
				
			||||||
  "modules/khors-graphics",
 | 
					  "modules/khors-graphics",
 | 
				
			||||||
  "modules/khors-window",
 | 
					  "modules/khors-window",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,8 @@
 | 
				
			||||||
use anyhow::Result;
 | 
					use anyhow::Result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use khors_config::ConfigModule;
 | 
					use khors_config::ConfigModule;
 | 
				
			||||||
use khors_app::App;
 | 
					use khors_app::app::App;
 | 
				
			||||||
use khors_graphics::RenderModule;
 | 
					use khors_graphics::renderer::RenderModule;
 | 
				
			||||||
use khors_window::WindowModule;
 | 
					use khors_window::WindowModule;
 | 
				
			||||||
use tokio::runtime::Builder;
 | 
					use tokio::runtime::Builder;
 | 
				
			||||||
use winit::event_loop::{ControlFlow, EventLoopBuilder};
 | 
					use winit::event_loop::{ControlFlow, EventLoopBuilder};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										318
									
								
								modules/khors-app/src/app.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								modules/khors-app/src/app.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,318 @@
 | 
				
			||||||
 | 
					#![warn(dead_code)]
 | 
				
			||||||
 | 
					use khors_core::{
 | 
				
			||||||
 | 
					    events::Events,
 | 
				
			||||||
 | 
					    module::{Module, ModulesStack},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use khors_graphics::{
 | 
				
			||||||
 | 
					    debug_gui::DebugGuiStack,
 | 
				
			||||||
 | 
					    render_module::{RenderModule as ThreadLocalModule, RenderModulesStack},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use anyhow::Result;
 | 
				
			||||||
 | 
					use flax::{component, Schedule, World};
 | 
				
			||||||
 | 
					use vulkano::device::DeviceFeatures;
 | 
				
			||||||
 | 
					use vulkano_util::{
 | 
				
			||||||
 | 
					    context::{VulkanoConfig, VulkanoContext},
 | 
				
			||||||
 | 
					    window::VulkanoWindows,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use winit::{
 | 
				
			||||||
 | 
					    event::{Event, WindowEvent},
 | 
				
			||||||
 | 
					    window::WindowId,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					component! {
 | 
				
			||||||
 | 
					    window_id: WindowId,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    resources,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[allow(dead_code)]
 | 
				
			||||||
 | 
					pub struct App {
 | 
				
			||||||
 | 
					    name: String,
 | 
				
			||||||
 | 
					    modules: ModulesStack,
 | 
				
			||||||
 | 
					    thread_local_modules: RenderModulesStack,
 | 
				
			||||||
 | 
					    world: World,
 | 
				
			||||||
 | 
					    schedule: Schedule,
 | 
				
			||||||
 | 
					    events: Events,
 | 
				
			||||||
 | 
					    rx: flume::Receiver<AppEvent>,
 | 
				
			||||||
 | 
					    running: bool,
 | 
				
			||||||
 | 
					    event_cleanup_time: std::time::Duration,
 | 
				
			||||||
 | 
					    vk_context: VulkanoContext,
 | 
				
			||||||
 | 
					    vk_windows: VulkanoWindows,
 | 
				
			||||||
 | 
					    debug_gui_stack: DebugGuiStack,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl App {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        let mut events = Events::new();
 | 
				
			||||||
 | 
					        let (tx, rx) = flume::unbounded();
 | 
				
			||||||
 | 
					        events.subscribe_custom(tx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let schedule = Schedule::builder().build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let vk_config = VulkanoConfig {
 | 
				
			||||||
 | 
					            device_features: DeviceFeatures {
 | 
				
			||||||
 | 
					                dynamic_rendering: true,
 | 
				
			||||||
 | 
					                ..Default::default()
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let vk_context = VulkanoContext::new(vk_config);
 | 
				
			||||||
 | 
					        let vk_windows = VulkanoWindows::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            name: "Khors".into(),
 | 
				
			||||||
 | 
					            modules: ModulesStack::new(),
 | 
				
			||||||
 | 
					            thread_local_modules: RenderModulesStack::new(),
 | 
				
			||||||
 | 
					            world: World::new(),
 | 
				
			||||||
 | 
					            schedule,
 | 
				
			||||||
 | 
					            events,
 | 
				
			||||||
 | 
					            rx,
 | 
				
			||||||
 | 
					            running: false,
 | 
				
			||||||
 | 
					            event_cleanup_time: std::time::Duration::from_secs(60),
 | 
				
			||||||
 | 
					            vk_context,
 | 
				
			||||||
 | 
					            vk_windows,
 | 
				
			||||||
 | 
					            debug_gui_stack: DebugGuiStack::default(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn run(&mut self) -> Result<()> {
 | 
				
			||||||
 | 
					        self.running = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.schedule.execute_par(&mut self.world).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let vk_context = &mut self.vk_context;
 | 
				
			||||||
 | 
					        let vk_windows = &mut self.vk_windows;
 | 
				
			||||||
 | 
					        let world = &mut self.world;
 | 
				
			||||||
 | 
					        let events = &mut self.events;
 | 
				
			||||||
 | 
					        let frame_time = std::time::Duration::from_millis(16);
 | 
				
			||||||
 | 
					        let gui_stack = &mut self.debug_gui_stack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for module in self.modules.iter_mut() {
 | 
				
			||||||
 | 
					            module.on_update(world, events, frame_time)?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for module in self.thread_local_modules.iter_mut() {
 | 
				
			||||||
 | 
					            module.on_update(gui_stack, vk_context, vk_windows, world, events, frame_time)?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.handle_events();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn create_window<T>(&mut self, event_loop: &winit::event_loop::EventLoopWindowTarget<T>)
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        T: Clone + Send + Sync,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let vk_window_id = self.vk_windows.create_window(
 | 
				
			||||||
 | 
					            event_loop,
 | 
				
			||||||
 | 
					            &self.vk_context,
 | 
				
			||||||
 | 
					            &vulkano_util::window::WindowDescriptor {
 | 
				
			||||||
 | 
					                title: self.name.clone(),
 | 
				
			||||||
 | 
					                present_mode: vulkano::swapchain::PresentMode::Mailbox,
 | 
				
			||||||
 | 
					                ..Default::default()
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            |_| {},
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let renderer = self.vk_windows.get_renderer(vk_window_id).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.world
 | 
				
			||||||
 | 
					            .set(resources(), window_id(), vk_window_id)
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.debug_gui_stack
 | 
				
			||||||
 | 
					            .add_gui(vk_window_id, event_loop, renderer, true, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn process_event_loop<T>(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        event: winit::event::Event<T>,
 | 
				
			||||||
 | 
					        _elwt: &winit::event_loop::EventLoopWindowTarget<T>,
 | 
				
			||||||
 | 
					    ) -> Result<bool>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        T: Clone + Send + Sync,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        match &event {
 | 
				
			||||||
 | 
					            Event::WindowEvent {
 | 
				
			||||||
 | 
					                event: WindowEvent::CloseRequested,
 | 
				
			||||||
 | 
					                ..
 | 
				
			||||||
 | 
					            } => {
 | 
				
			||||||
 | 
					                return Ok(true);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Event::WindowEvent {
 | 
				
			||||||
 | 
					                event: WindowEvent::Focused(_),
 | 
				
			||||||
 | 
					                ..
 | 
				
			||||||
 | 
					            } => self.events().send(event.clone()),
 | 
				
			||||||
 | 
					            Event::WindowEvent {
 | 
				
			||||||
 | 
					                event: WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. },
 | 
				
			||||||
 | 
					                window_id,
 | 
				
			||||||
 | 
					            } => self
 | 
				
			||||||
 | 
					                .vk_windows
 | 
				
			||||||
 | 
					                .get_renderer_mut(*window_id)
 | 
				
			||||||
 | 
					                .unwrap()
 | 
				
			||||||
 | 
					                .resize(),
 | 
				
			||||||
 | 
					            Event::WindowEvent {
 | 
				
			||||||
 | 
					                event: WindowEvent::RedrawRequested,
 | 
				
			||||||
 | 
					                window_id,
 | 
				
			||||||
 | 
					            } => 'redraw: {
 | 
				
			||||||
 | 
					                // Tasks for redrawing:
 | 
				
			||||||
 | 
					                // 1. Update state based on events
 | 
				
			||||||
 | 
					                // 2. Compute & Render
 | 
				
			||||||
 | 
					                // 3. Reset input state
 | 
				
			||||||
 | 
					                // 4. Update time & title
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // The rendering part goes here:
 | 
				
			||||||
 | 
					                match self
 | 
				
			||||||
 | 
					                    .vk_windows
 | 
				
			||||||
 | 
					                    .get_renderer(*window_id)
 | 
				
			||||||
 | 
					                    .unwrap()
 | 
				
			||||||
 | 
					                    .window_size()
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    [w, h] => {
 | 
				
			||||||
 | 
					                        // Skip this frame when minimized.
 | 
				
			||||||
 | 
					                        if w == 0.0 || h == 0.0 {
 | 
				
			||||||
 | 
					                            break 'redraw;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                self.run()?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Event::WindowEvent { window_id, event } => {
 | 
				
			||||||
 | 
					                let window = self.vk_windows.get_window(*window_id).unwrap();
 | 
				
			||||||
 | 
					                let gui = self.debug_gui_stack.get_mut(*window_id).unwrap();
 | 
				
			||||||
 | 
					                gui.update(window, event);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Event::AboutToWait => {
 | 
				
			||||||
 | 
					                self.vk_windows.iter().for_each(|(window_id, _)| {
 | 
				
			||||||
 | 
					                    self.vk_windows
 | 
				
			||||||
 | 
					                        .get_window(*window_id)
 | 
				
			||||||
 | 
					                        .unwrap()
 | 
				
			||||||
 | 
					                        .request_redraw()
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _ => (),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok(false)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn handle_events(&mut self) {
 | 
				
			||||||
 | 
					        for event in self.rx.try_iter() {
 | 
				
			||||||
 | 
					            match event {
 | 
				
			||||||
 | 
					                AppEvent::Exit => self.running = false,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[allow(dead_code)]
 | 
				
			||||||
 | 
					    pub fn set_schedule(&mut self, schedule: Schedule) {
 | 
				
			||||||
 | 
					        self.schedule = schedule;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[allow(dead_code)]
 | 
				
			||||||
 | 
					    pub fn world(&self) -> &World {
 | 
				
			||||||
 | 
					        &self.world
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[allow(dead_code)]
 | 
				
			||||||
 | 
					    pub fn world_mut(&mut self) -> &mut World {
 | 
				
			||||||
 | 
					        &mut self.world
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn events(&self) -> &Events {
 | 
				
			||||||
 | 
					        &self.events
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[allow(dead_code)]
 | 
				
			||||||
 | 
					    pub fn events_mut(&mut self) -> &mut Events {
 | 
				
			||||||
 | 
					        &mut self.events
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Pushes a module from the provided init closure to to the top of the layer stack. The provided
 | 
				
			||||||
 | 
					    /// closure to construct the layer takes in the world and events.
 | 
				
			||||||
 | 
					    pub fn push_render_module<F, T>(&mut self, func: F)
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        F: FnOnce(
 | 
				
			||||||
 | 
					            &mut VulkanoContext,
 | 
				
			||||||
 | 
					            &mut VulkanoWindows,
 | 
				
			||||||
 | 
					            &mut Schedule,
 | 
				
			||||||
 | 
					            &mut World,
 | 
				
			||||||
 | 
					            &mut Events,
 | 
				
			||||||
 | 
					        ) -> T,
 | 
				
			||||||
 | 
					        T: 'static + ThreadLocalModule,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let module = func(
 | 
				
			||||||
 | 
					            &mut self.vk_context,
 | 
				
			||||||
 | 
					            &mut self.vk_windows,
 | 
				
			||||||
 | 
					            &mut self.schedule,
 | 
				
			||||||
 | 
					            &mut self.world,
 | 
				
			||||||
 | 
					            &mut self.events,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        self.thread_local_modules.push(module);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Pushes a layer from the provided init closure to to the top of the layer stack. The provided
 | 
				
			||||||
 | 
					    /// closure to construct the layer takes in the world and events.
 | 
				
			||||||
 | 
					    pub fn push_module<F, T>(&mut self, func: F)
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        F: FnOnce(&mut Schedule, &mut World, &mut Events) -> T,
 | 
				
			||||||
 | 
					        T: 'static + Module,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let module = func(&mut self.schedule, &mut self.world, &mut self.events);
 | 
				
			||||||
 | 
					        self.modules.push(module);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Pushes a module from the provided init closure to to the top of the module stack. The provided
 | 
				
			||||||
 | 
					    /// closure to construct the module takes in the world and events, and may return an error which
 | 
				
			||||||
 | 
					    /// is propagated to the callee.
 | 
				
			||||||
 | 
					    #[allow(dead_code)]
 | 
				
			||||||
 | 
					    pub fn try_push_module<F, T, E>(&mut self, func: F) -> Result<(), E>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        F: FnOnce(&mut World, &mut Events) -> Result<T, E>,
 | 
				
			||||||
 | 
					        T: 'static + Module,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let module = func(&mut self.world, &mut self.events)?;
 | 
				
			||||||
 | 
					        self.modules.push(module);
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Inserts a module from the provided init closure to to the top of the module stack. The provided
 | 
				
			||||||
 | 
					    /// closure to construct the module takes in the world and events.
 | 
				
			||||||
 | 
					    #[allow(dead_code)]
 | 
				
			||||||
 | 
					    pub fn insert_module<F, T>(&mut self, index: usize, func: F)
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        F: FnOnce(&mut World, &mut Events) -> T,
 | 
				
			||||||
 | 
					        T: 'static + Module,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let module = func(&mut self.world, &mut self.events);
 | 
				
			||||||
 | 
					        self.modules.insert(index, module);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Pushes a module from the provided init closure to to the top of the module stack. The provided
 | 
				
			||||||
 | 
					    /// closure to construct the module takes in the world and events, and may return an error which
 | 
				
			||||||
 | 
					    /// is propagated to the callee.
 | 
				
			||||||
 | 
					    #[allow(dead_code)]
 | 
				
			||||||
 | 
					    pub fn try_insert_module<F, T, E>(&mut self, index: usize, func: F) -> Result<(), E>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        F: FnOnce(&mut World, &mut Events) -> Result<T, E>,
 | 
				
			||||||
 | 
					        T: 'static + Module,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let module = func(&mut self.world, &mut self.events)?;
 | 
				
			||||||
 | 
					        self.modules.insert(index, module);
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy, PartialEq)]
 | 
				
			||||||
 | 
					#[allow(dead_code)]
 | 
				
			||||||
 | 
					pub enum AppEvent {
 | 
				
			||||||
 | 
					    Exit,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for App {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self::new()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,318 +1 @@
 | 
				
			||||||
#![warn(dead_code)]
 | 
					pub mod app;
 | 
				
			||||||
use khors_core::{
 | 
					 | 
				
			||||||
    events::Events,
 | 
					 | 
				
			||||||
    module::{Module, ModulesStack},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use khors_graphics::{
 | 
					 | 
				
			||||||
    debug_gui::DebugGuiStack,
 | 
					 | 
				
			||||||
    render_module::{RenderModule as ThreadLocalModule, RenderModulesStack},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use anyhow::Result;
 | 
					 | 
				
			||||||
use flax::{component, Schedule, World};
 | 
					 | 
				
			||||||
use vulkano::device::DeviceFeatures;
 | 
					 | 
				
			||||||
use vulkano_util::{
 | 
					 | 
				
			||||||
    context::{VulkanoConfig, VulkanoContext},
 | 
					 | 
				
			||||||
    window::VulkanoWindows,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use winit::{
 | 
					 | 
				
			||||||
    event::{Event, WindowEvent},
 | 
					 | 
				
			||||||
    window::WindowId,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
component! {
 | 
					 | 
				
			||||||
    window_id: WindowId,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    resources,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[allow(dead_code)]
 | 
					 | 
				
			||||||
pub struct App {
 | 
					 | 
				
			||||||
    name: String,
 | 
					 | 
				
			||||||
    modules: ModulesStack,
 | 
					 | 
				
			||||||
    thread_local_modules: RenderModulesStack,
 | 
					 | 
				
			||||||
    world: World,
 | 
					 | 
				
			||||||
    schedule: Schedule,
 | 
					 | 
				
			||||||
    events: Events,
 | 
					 | 
				
			||||||
    rx: flume::Receiver<AppEvent>,
 | 
					 | 
				
			||||||
    running: bool,
 | 
					 | 
				
			||||||
    event_cleanup_time: std::time::Duration,
 | 
					 | 
				
			||||||
    vk_context: VulkanoContext,
 | 
					 | 
				
			||||||
    vk_windows: VulkanoWindows,
 | 
					 | 
				
			||||||
    debug_gui_stack: DebugGuiStack,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl App {
 | 
					 | 
				
			||||||
    pub fn new() -> Self {
 | 
					 | 
				
			||||||
        let mut events = Events::new();
 | 
					 | 
				
			||||||
        let (tx, rx) = flume::unbounded();
 | 
					 | 
				
			||||||
        events.subscribe_custom(tx);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let schedule = Schedule::builder().build();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let vk_config = VulkanoConfig {
 | 
					 | 
				
			||||||
            device_features: DeviceFeatures {
 | 
					 | 
				
			||||||
                dynamic_rendering: true,
 | 
					 | 
				
			||||||
                ..Default::default()
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            ..Default::default()
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        let vk_context = VulkanoContext::new(vk_config);
 | 
					 | 
				
			||||||
        let vk_windows = VulkanoWindows::default();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            name: "Khors".into(),
 | 
					 | 
				
			||||||
            modules: ModulesStack::new(),
 | 
					 | 
				
			||||||
            thread_local_modules: RenderModulesStack::new(),
 | 
					 | 
				
			||||||
            world: World::new(),
 | 
					 | 
				
			||||||
            schedule,
 | 
					 | 
				
			||||||
            events,
 | 
					 | 
				
			||||||
            rx,
 | 
					 | 
				
			||||||
            running: false,
 | 
					 | 
				
			||||||
            event_cleanup_time: std::time::Duration::from_secs(60),
 | 
					 | 
				
			||||||
            vk_context,
 | 
					 | 
				
			||||||
            vk_windows,
 | 
					 | 
				
			||||||
            debug_gui_stack: DebugGuiStack::default(),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn run(&mut self) -> Result<()> {
 | 
					 | 
				
			||||||
        self.running = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.schedule.execute_par(&mut self.world).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let vk_context = &mut self.vk_context;
 | 
					 | 
				
			||||||
        let vk_windows = &mut self.vk_windows;
 | 
					 | 
				
			||||||
        let world = &mut self.world;
 | 
					 | 
				
			||||||
        let events = &mut self.events;
 | 
					 | 
				
			||||||
        let frame_time = std::time::Duration::from_millis(16);
 | 
					 | 
				
			||||||
        let gui_stack = &mut self.debug_gui_stack;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for module in self.modules.iter_mut() {
 | 
					 | 
				
			||||||
            module.on_update(world, events, frame_time)?;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for module in self.thread_local_modules.iter_mut() {
 | 
					 | 
				
			||||||
            module.on_update(gui_stack, vk_context, vk_windows, world, events, frame_time)?;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.handle_events();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn create_window<T>(&mut self, event_loop: &winit::event_loop::EventLoopWindowTarget<T>)
 | 
					 | 
				
			||||||
    where
 | 
					 | 
				
			||||||
        T: Clone + Send + Sync,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        let vk_window_id = self.vk_windows.create_window(
 | 
					 | 
				
			||||||
            event_loop,
 | 
					 | 
				
			||||||
            &self.vk_context,
 | 
					 | 
				
			||||||
            &vulkano_util::window::WindowDescriptor {
 | 
					 | 
				
			||||||
                title: self.name.clone(),
 | 
					 | 
				
			||||||
                present_mode: vulkano::swapchain::PresentMode::Mailbox,
 | 
					 | 
				
			||||||
                ..Default::default()
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            |_| {},
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let renderer = self.vk_windows.get_renderer(vk_window_id).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.world
 | 
					 | 
				
			||||||
            .set(resources(), window_id(), vk_window_id)
 | 
					 | 
				
			||||||
            .unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.debug_gui_stack
 | 
					 | 
				
			||||||
            .add_gui(vk_window_id, event_loop, renderer, true, false);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn process_event_loop<T>(
 | 
					 | 
				
			||||||
        &mut self,
 | 
					 | 
				
			||||||
        event: winit::event::Event<T>,
 | 
					 | 
				
			||||||
        _elwt: &winit::event_loop::EventLoopWindowTarget<T>,
 | 
					 | 
				
			||||||
    ) -> Result<bool>
 | 
					 | 
				
			||||||
    where
 | 
					 | 
				
			||||||
        T: Clone + Send + Sync,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        match &event {
 | 
					 | 
				
			||||||
            Event::WindowEvent {
 | 
					 | 
				
			||||||
                event: WindowEvent::CloseRequested,
 | 
					 | 
				
			||||||
                ..
 | 
					 | 
				
			||||||
            } => {
 | 
					 | 
				
			||||||
                return Ok(true);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Event::WindowEvent {
 | 
					 | 
				
			||||||
                event: WindowEvent::Focused(_),
 | 
					 | 
				
			||||||
                ..
 | 
					 | 
				
			||||||
            } => self.events().send(event.clone()),
 | 
					 | 
				
			||||||
            Event::WindowEvent {
 | 
					 | 
				
			||||||
                event: WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. },
 | 
					 | 
				
			||||||
                window_id,
 | 
					 | 
				
			||||||
            } => self
 | 
					 | 
				
			||||||
                .vk_windows
 | 
					 | 
				
			||||||
                .get_renderer_mut(*window_id)
 | 
					 | 
				
			||||||
                .unwrap()
 | 
					 | 
				
			||||||
                .resize(),
 | 
					 | 
				
			||||||
            Event::WindowEvent {
 | 
					 | 
				
			||||||
                event: WindowEvent::RedrawRequested,
 | 
					 | 
				
			||||||
                window_id,
 | 
					 | 
				
			||||||
            } => 'redraw: {
 | 
					 | 
				
			||||||
                // Tasks for redrawing:
 | 
					 | 
				
			||||||
                // 1. Update state based on events
 | 
					 | 
				
			||||||
                // 2. Compute & Render
 | 
					 | 
				
			||||||
                // 3. Reset input state
 | 
					 | 
				
			||||||
                // 4. Update time & title
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // The rendering part goes here:
 | 
					 | 
				
			||||||
                match self
 | 
					 | 
				
			||||||
                    .vk_windows
 | 
					 | 
				
			||||||
                    .get_renderer(*window_id)
 | 
					 | 
				
			||||||
                    .unwrap()
 | 
					 | 
				
			||||||
                    .window_size()
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    [w, h] => {
 | 
					 | 
				
			||||||
                        // Skip this frame when minimized.
 | 
					 | 
				
			||||||
                        if w == 0.0 || h == 0.0 {
 | 
					 | 
				
			||||||
                            break 'redraw;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                self.run()?;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Event::WindowEvent { window_id, event } => {
 | 
					 | 
				
			||||||
                let window = self.vk_windows.get_window(*window_id).unwrap();
 | 
					 | 
				
			||||||
                let gui = self.debug_gui_stack.get_mut(*window_id).unwrap();
 | 
					 | 
				
			||||||
                gui.update(window, event);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Event::AboutToWait => {
 | 
					 | 
				
			||||||
                self.vk_windows.iter().for_each(|(window_id, _)| {
 | 
					 | 
				
			||||||
                    self.vk_windows
 | 
					 | 
				
			||||||
                        .get_window(*window_id)
 | 
					 | 
				
			||||||
                        .unwrap()
 | 
					 | 
				
			||||||
                        .request_redraw()
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            _ => (),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        Ok(false)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn handle_events(&mut self) {
 | 
					 | 
				
			||||||
        for event in self.rx.try_iter() {
 | 
					 | 
				
			||||||
            match event {
 | 
					 | 
				
			||||||
                AppEvent::Exit => self.running = false,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[allow(dead_code)]
 | 
					 | 
				
			||||||
    pub fn set_schedule(&mut self, schedule: Schedule) {
 | 
					 | 
				
			||||||
        self.schedule = schedule;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[allow(dead_code)]
 | 
					 | 
				
			||||||
    pub fn world(&self) -> &World {
 | 
					 | 
				
			||||||
        &self.world
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[allow(dead_code)]
 | 
					 | 
				
			||||||
    pub fn world_mut(&mut self) -> &mut World {
 | 
					 | 
				
			||||||
        &mut self.world
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn events(&self) -> &Events {
 | 
					 | 
				
			||||||
        &self.events
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[allow(dead_code)]
 | 
					 | 
				
			||||||
    pub fn events_mut(&mut self) -> &mut Events {
 | 
					 | 
				
			||||||
        &mut self.events
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Pushes a module from the provided init closure to to the top of the layer stack. The provided
 | 
					 | 
				
			||||||
    /// closure to construct the layer takes in the world and events.
 | 
					 | 
				
			||||||
    pub fn push_render_module<F, T>(&mut self, func: F)
 | 
					 | 
				
			||||||
    where
 | 
					 | 
				
			||||||
        F: FnOnce(
 | 
					 | 
				
			||||||
            &mut VulkanoContext,
 | 
					 | 
				
			||||||
            &mut VulkanoWindows,
 | 
					 | 
				
			||||||
            &mut Schedule,
 | 
					 | 
				
			||||||
            &mut World,
 | 
					 | 
				
			||||||
            &mut Events,
 | 
					 | 
				
			||||||
        ) -> T,
 | 
					 | 
				
			||||||
        T: 'static + ThreadLocalModule,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        let module = func(
 | 
					 | 
				
			||||||
            &mut self.vk_context,
 | 
					 | 
				
			||||||
            &mut self.vk_windows,
 | 
					 | 
				
			||||||
            &mut self.schedule,
 | 
					 | 
				
			||||||
            &mut self.world,
 | 
					 | 
				
			||||||
            &mut self.events,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        self.thread_local_modules.push(module);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Pushes a layer from the provided init closure to to the top of the layer stack. The provided
 | 
					 | 
				
			||||||
    /// closure to construct the layer takes in the world and events.
 | 
					 | 
				
			||||||
    pub fn push_module<F, T>(&mut self, func: F)
 | 
					 | 
				
			||||||
    where
 | 
					 | 
				
			||||||
        F: FnOnce(&mut Schedule, &mut World, &mut Events) -> T,
 | 
					 | 
				
			||||||
        T: 'static + Module,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        let module = func(&mut self.schedule, &mut self.world, &mut self.events);
 | 
					 | 
				
			||||||
        self.modules.push(module);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Pushes a module from the provided init closure to to the top of the module stack. The provided
 | 
					 | 
				
			||||||
    /// closure to construct the module takes in the world and events, and may return an error which
 | 
					 | 
				
			||||||
    /// is propagated to the callee.
 | 
					 | 
				
			||||||
    #[allow(dead_code)]
 | 
					 | 
				
			||||||
    pub fn try_push_module<F, T, E>(&mut self, func: F) -> Result<(), E>
 | 
					 | 
				
			||||||
    where
 | 
					 | 
				
			||||||
        F: FnOnce(&mut World, &mut Events) -> Result<T, E>,
 | 
					 | 
				
			||||||
        T: 'static + Module,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        let module = func(&mut self.world, &mut self.events)?;
 | 
					 | 
				
			||||||
        self.modules.push(module);
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Inserts a module from the provided init closure to to the top of the module stack. The provided
 | 
					 | 
				
			||||||
    /// closure to construct the module takes in the world and events.
 | 
					 | 
				
			||||||
    #[allow(dead_code)]
 | 
					 | 
				
			||||||
    pub fn insert_module<F, T>(&mut self, index: usize, func: F)
 | 
					 | 
				
			||||||
    where
 | 
					 | 
				
			||||||
        F: FnOnce(&mut World, &mut Events) -> T,
 | 
					 | 
				
			||||||
        T: 'static + Module,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        let module = func(&mut self.world, &mut self.events);
 | 
					 | 
				
			||||||
        self.modules.insert(index, module);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Pushes a module from the provided init closure to to the top of the module stack. The provided
 | 
					 | 
				
			||||||
    /// closure to construct the module takes in the world and events, and may return an error which
 | 
					 | 
				
			||||||
    /// is propagated to the callee.
 | 
					 | 
				
			||||||
    #[allow(dead_code)]
 | 
					 | 
				
			||||||
    pub fn try_insert_module<F, T, E>(&mut self, index: usize, func: F) -> Result<(), E>
 | 
					 | 
				
			||||||
    where
 | 
					 | 
				
			||||||
        F: FnOnce(&mut World, &mut Events) -> Result<T, E>,
 | 
					 | 
				
			||||||
        T: 'static + Module,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        let module = func(&mut self.world, &mut self.events)?;
 | 
					 | 
				
			||||||
        self.modules.insert(index, module);
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug, Clone, Copy, PartialEq)]
 | 
					 | 
				
			||||||
#[allow(dead_code)]
 | 
					 | 
				
			||||||
pub enum AppEvent {
 | 
					 | 
				
			||||||
    Exit,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Default for App {
 | 
					 | 
				
			||||||
    fn default() -> Self {
 | 
					 | 
				
			||||||
        Self::new()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,9 +8,12 @@ edition = "2021"
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
khors-core = { path = "../../khors-core", version = "0.1.0" }
 | 
					khors-core = { path = "../../khors-core", version = "0.1.0" }
 | 
				
			||||||
egui-vulkano = { path = "../../vendor/egui-vulkano", version = "0.1.0" }
 | 
					egui-vulkano = { path = "../../vendor/egui-vulkano", version = "0.1.0" }
 | 
				
			||||||
 | 
					egui-snarl = { path = "../../vendor/egui-snarl", features = ["serde"] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
anyhow = "1.0.80"
 | 
					anyhow = "1.0.80"
 | 
				
			||||||
 | 
					thiserror = "1.0.58"
 | 
				
			||||||
egui = "0.27.1"
 | 
					egui = "0.27.1"
 | 
				
			||||||
 | 
					syn = "2.0.58"
 | 
				
			||||||
glam = "0.27.0"
 | 
					glam = "0.27.0"
 | 
				
			||||||
winit = { version = "0.29.15",features = ["rwh_05"] }
 | 
					winit = { version = "0.29.15",features = ["rwh_05"] }
 | 
				
			||||||
vulkano = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" }
 | 
					vulkano = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,555 +1,10 @@
 | 
				
			||||||
pub mod debug_gui;
 | 
					pub mod debug_gui;
 | 
				
			||||||
pub mod render_module;
 | 
					pub mod render_module;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use flax::{entity_ids, BoxedSystem, Query, QueryBorrow, Schedule, System, World};
 | 
					 | 
				
			||||||
use glam::{
 | 
					 | 
				
			||||||
    f32::{Mat3, Vec3},
 | 
					 | 
				
			||||||
    Mat4,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use std::sync::Arc;
 | 
					 | 
				
			||||||
use vulkano::{
 | 
					 | 
				
			||||||
    buffer::{
 | 
					 | 
				
			||||||
        allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo},
 | 
					 | 
				
			||||||
        Buffer, BufferCreateInfo, BufferUsage,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    command_buffer::{
 | 
					 | 
				
			||||||
        allocator::{CommandBufferAllocator, StandardCommandBufferAllocator},
 | 
					 | 
				
			||||||
        CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, RecordingCommandBuffer,
 | 
					 | 
				
			||||||
        RenderPassBeginInfo,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    descriptor_set::{
 | 
					 | 
				
			||||||
        allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    device::DeviceOwned,
 | 
					 | 
				
			||||||
    format::Format,
 | 
					 | 
				
			||||||
    image::{view::ImageView, Image, ImageCreateInfo, ImageType, ImageUsage},
 | 
					 | 
				
			||||||
    memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
 | 
					 | 
				
			||||||
    pipeline::{
 | 
					 | 
				
			||||||
        graphics::{
 | 
					 | 
				
			||||||
            color_blend::{ColorBlendAttachmentState, ColorBlendState},
 | 
					 | 
				
			||||||
            depth_stencil::{DepthState, DepthStencilState},
 | 
					 | 
				
			||||||
            input_assembly::InputAssemblyState,
 | 
					 | 
				
			||||||
            multisample::MultisampleState,
 | 
					 | 
				
			||||||
            rasterization::RasterizationState,
 | 
					 | 
				
			||||||
            vertex_input::{Vertex, VertexDefinition},
 | 
					 | 
				
			||||||
            viewport::{Viewport, ViewportState},
 | 
					 | 
				
			||||||
            GraphicsPipelineCreateInfo,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        layout::PipelineDescriptorSetLayoutCreateInfo,
 | 
					 | 
				
			||||||
        GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout,
 | 
					 | 
				
			||||||
        PipelineShaderStageCreateInfo,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
 | 
					 | 
				
			||||||
    shader::EntryPoint,
 | 
					 | 
				
			||||||
    sync::GpuFuture,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use vulkano_util::{
 | 
					 | 
				
			||||||
    context::VulkanoContext, renderer::VulkanoWindowRenderer, window::VulkanoWindows,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use egui_vulkano::Gui;
 | 
					 | 
				
			||||||
use crate::debug_gui::DebugGuiStack;
 | 
					 | 
				
			||||||
use crate::render_module::RenderModule as ThreadLocalModule;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use self::{
 | 
					 | 
				
			||||||
    model::{INDICES, NORMALS, POSITIONS},
 | 
					 | 
				
			||||||
    vulkan::vertex::{Normal, Position},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub mod events;
 | 
					pub mod events;
 | 
				
			||||||
mod model;
 | 
					pub mod renderer;
 | 
				
			||||||
 | 
					pub mod rendergraph;
 | 
				
			||||||
 | 
					pub mod node_editor;
 | 
				
			||||||
 | 
					mod temp;
 | 
				
			||||||
mod test_pipeline;
 | 
					mod test_pipeline;
 | 
				
			||||||
mod vulkan;
 | 
					mod vulkan;
 | 
				
			||||||
 | 
					 | 
				
			||||||
pub struct RenderModule {
 | 
					 | 
				
			||||||
    schedule: Schedule,
 | 
					 | 
				
			||||||
    memory_allocator: Arc<StandardMemoryAllocator>,
 | 
					 | 
				
			||||||
    descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
 | 
					 | 
				
			||||||
    command_buffer_allocator: Arc<dyn CommandBufferAllocator>,
 | 
					 | 
				
			||||||
    viewport: Viewport,
 | 
					 | 
				
			||||||
    rotation_start: std::time::Instant,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl RenderModule {
 | 
					 | 
				
			||||||
    pub fn new(
 | 
					 | 
				
			||||||
        vk_context: &mut VulkanoContext,
 | 
					 | 
				
			||||||
        _vk_windows: &mut VulkanoWindows,
 | 
					 | 
				
			||||||
        _schedule: &mut Schedule,
 | 
					 | 
				
			||||||
        _world: &mut World,
 | 
					 | 
				
			||||||
        _events: &mut khors_core::events::Events,
 | 
					 | 
				
			||||||
    ) -> Self {
 | 
					 | 
				
			||||||
        let schedule = Schedule::builder()
 | 
					 | 
				
			||||||
            .with_system(add_distance_system())
 | 
					 | 
				
			||||||
            .build();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(
 | 
					 | 
				
			||||||
            vk_context.device().clone(),
 | 
					 | 
				
			||||||
        ));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
 | 
					 | 
				
			||||||
            vk_context.device().clone(),
 | 
					 | 
				
			||||||
            Default::default(),
 | 
					 | 
				
			||||||
        ));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
 | 
					 | 
				
			||||||
            vk_context.device().clone(),
 | 
					 | 
				
			||||||
            Default::default(),
 | 
					 | 
				
			||||||
        ));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let viewport = Viewport {
 | 
					 | 
				
			||||||
            offset: [0.0, 0.0],
 | 
					 | 
				
			||||||
            extent: [0.0, 0.0],
 | 
					 | 
				
			||||||
            depth_range: 0.0..=1.0,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let rotation_start = std::time::Instant::now();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            schedule,
 | 
					 | 
				
			||||||
            memory_allocator,
 | 
					 | 
				
			||||||
            descriptor_set_allocator,
 | 
					 | 
				
			||||||
            command_buffer_allocator,
 | 
					 | 
				
			||||||
            viewport,
 | 
					 | 
				
			||||||
            rotation_start,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl ThreadLocalModule for RenderModule {
 | 
					 | 
				
			||||||
    fn on_update(
 | 
					 | 
				
			||||||
        &mut self,
 | 
					 | 
				
			||||||
        gui_stack: &mut DebugGuiStack,
 | 
					 | 
				
			||||||
        vk_context: &mut VulkanoContext,
 | 
					 | 
				
			||||||
        vk_windows: &mut vulkano_util::window::VulkanoWindows,
 | 
					 | 
				
			||||||
        world: &mut World,
 | 
					 | 
				
			||||||
        _events: &mut khors_core::events::Events,
 | 
					 | 
				
			||||||
        _frame_time: std::time::Duration,
 | 
					 | 
				
			||||||
    ) -> anyhow::Result<()> {
 | 
					 | 
				
			||||||
        self.schedule.execute_seq(world).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let viewport = &mut self.viewport;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (window_id, renderer) in vk_windows.iter_mut() {
 | 
					 | 
				
			||||||
            let gui = gui_stack.get_mut(*window_id).unwrap();
 | 
					 | 
				
			||||||
            draw(
 | 
					 | 
				
			||||||
                vk_context.device().clone(),
 | 
					 | 
				
			||||||
                self.memory_allocator.clone(),
 | 
					 | 
				
			||||||
                self.descriptor_set_allocator.clone(),
 | 
					 | 
				
			||||||
                self.command_buffer_allocator.clone(),
 | 
					 | 
				
			||||||
                viewport,
 | 
					 | 
				
			||||||
                vk_context,
 | 
					 | 
				
			||||||
                renderer,
 | 
					 | 
				
			||||||
                gui,
 | 
					 | 
				
			||||||
                self.rotation_start,
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub fn add_distance_system() -> BoxedSystem {
 | 
					 | 
				
			||||||
    let query = Query::new(entity_ids());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    System::builder()
 | 
					 | 
				
			||||||
        .with_query(query)
 | 
					 | 
				
			||||||
        .build(|mut query: QueryBorrow<'_, flax::EntityIds, _>| {
 | 
					 | 
				
			||||||
            for _id in &mut query {
 | 
					 | 
				
			||||||
                // println!("----------: {}", _id.index());
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .boxed()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn draw(
 | 
					 | 
				
			||||||
    device: Arc<vulkano::device::Device>,
 | 
					 | 
				
			||||||
    memory_allocator: Arc<StandardMemoryAllocator>,
 | 
					 | 
				
			||||||
    descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
 | 
					 | 
				
			||||||
    command_buffer_allocator: Arc<dyn CommandBufferAllocator>,
 | 
					 | 
				
			||||||
    _viewport: &mut Viewport,
 | 
					 | 
				
			||||||
    context: &mut VulkanoContext,
 | 
					 | 
				
			||||||
    renderer: &mut VulkanoWindowRenderer,
 | 
					 | 
				
			||||||
    gui: &mut Gui,
 | 
					 | 
				
			||||||
    rotation_start: std::time::Instant,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    let vertex_buffer = Buffer::from_iter(
 | 
					 | 
				
			||||||
        memory_allocator.clone(),
 | 
					 | 
				
			||||||
        BufferCreateInfo {
 | 
					 | 
				
			||||||
            usage: BufferUsage::VERTEX_BUFFER,
 | 
					 | 
				
			||||||
            ..Default::default()
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        AllocationCreateInfo {
 | 
					 | 
				
			||||||
            memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
 | 
					 | 
				
			||||||
                | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
 | 
					 | 
				
			||||||
            ..Default::default()
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        POSITIONS,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    .unwrap();
 | 
					 | 
				
			||||||
    let normals_buffer = Buffer::from_iter(
 | 
					 | 
				
			||||||
        memory_allocator.clone(),
 | 
					 | 
				
			||||||
        BufferCreateInfo {
 | 
					 | 
				
			||||||
            usage: BufferUsage::VERTEX_BUFFER,
 | 
					 | 
				
			||||||
            ..Default::default()
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        AllocationCreateInfo {
 | 
					 | 
				
			||||||
            memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
 | 
					 | 
				
			||||||
                | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
 | 
					 | 
				
			||||||
            ..Default::default()
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        NORMALS,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    .unwrap();
 | 
					 | 
				
			||||||
    let index_buffer = Buffer::from_iter(
 | 
					 | 
				
			||||||
        memory_allocator.clone(),
 | 
					 | 
				
			||||||
        BufferCreateInfo {
 | 
					 | 
				
			||||||
            usage: BufferUsage::INDEX_BUFFER,
 | 
					 | 
				
			||||||
            ..Default::default()
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        AllocationCreateInfo {
 | 
					 | 
				
			||||||
            memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
 | 
					 | 
				
			||||||
                | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
 | 
					 | 
				
			||||||
            ..Default::default()
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        INDICES,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    .unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let uniform_buffer = SubbufferAllocator::new(
 | 
					 | 
				
			||||||
        memory_allocator.clone(),
 | 
					 | 
				
			||||||
        SubbufferAllocatorCreateInfo {
 | 
					 | 
				
			||||||
            buffer_usage: BufferUsage::UNIFORM_BUFFER,
 | 
					 | 
				
			||||||
            memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
 | 
					 | 
				
			||||||
                | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
 | 
					 | 
				
			||||||
            ..Default::default()
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let render_pass = vulkano::single_pass_renderpass!(
 | 
					 | 
				
			||||||
        device.clone(),
 | 
					 | 
				
			||||||
        attachments: {
 | 
					 | 
				
			||||||
            color: {
 | 
					 | 
				
			||||||
                format: renderer.swapchain_format(),
 | 
					 | 
				
			||||||
                samples: 1,
 | 
					 | 
				
			||||||
                load_op: Clear,
 | 
					 | 
				
			||||||
                store_op: Store,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            depth_stencil: {
 | 
					 | 
				
			||||||
                format: Format::D16_UNORM,
 | 
					 | 
				
			||||||
                samples: 1,
 | 
					 | 
				
			||||||
                load_op: Clear,
 | 
					 | 
				
			||||||
                store_op: DontCare,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        pass: {
 | 
					 | 
				
			||||||
            color: [color],
 | 
					 | 
				
			||||||
            depth_stencil: {depth_stencil},
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    .unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let vs = vs::load(device.clone())
 | 
					 | 
				
			||||||
        .unwrap()
 | 
					 | 
				
			||||||
        .entry_point("main")
 | 
					 | 
				
			||||||
        .unwrap();
 | 
					 | 
				
			||||||
    let fs = fs::load(device.clone())
 | 
					 | 
				
			||||||
        .unwrap()
 | 
					 | 
				
			||||||
        .entry_point("main")
 | 
					 | 
				
			||||||
        .unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let (mut pipeline, mut framebuffers) = window_size_dependent_setup(
 | 
					 | 
				
			||||||
        memory_allocator.clone(),
 | 
					 | 
				
			||||||
        vs.clone(),
 | 
					 | 
				
			||||||
        fs.clone(),
 | 
					 | 
				
			||||||
        renderer.swapchain_image_views(),
 | 
					 | 
				
			||||||
        render_pass.clone(),
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Do not draw the frame when the screen size is zero. On Windows, this can
 | 
					 | 
				
			||||||
    // occur when minimizing the application.
 | 
					 | 
				
			||||||
    let image_extent: [u32; 2] = renderer.window().inner_size().into();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if image_extent.contains(&0) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Begin rendering by acquiring the gpu future from the window renderer.
 | 
					 | 
				
			||||||
    let previous_frame_end = renderer
 | 
					 | 
				
			||||||
        .acquire(None, |swapchain_images| {
 | 
					 | 
				
			||||||
            // Whenever the window resizes we need to recreate everything dependent
 | 
					 | 
				
			||||||
            // on the window size. In this example that
 | 
					 | 
				
			||||||
            // includes the swapchain, the framebuffers
 | 
					 | 
				
			||||||
            // and the dynamic state viewport.
 | 
					 | 
				
			||||||
            let (new_pipeline, new_framebuffers) = window_size_dependent_setup(
 | 
					 | 
				
			||||||
                memory_allocator.clone(),
 | 
					 | 
				
			||||||
                vs.clone(),
 | 
					 | 
				
			||||||
                fs.clone(),
 | 
					 | 
				
			||||||
                swapchain_images,
 | 
					 | 
				
			||||||
                render_pass.clone(),
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            pipeline = new_pipeline;
 | 
					 | 
				
			||||||
            framebuffers = new_framebuffers;
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let uniform_buffer_subbuffer = {
 | 
					 | 
				
			||||||
        let elapsed = rotation_start.elapsed();
 | 
					 | 
				
			||||||
        let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0;
 | 
					 | 
				
			||||||
        let rotation = Mat3::from_rotation_y(rotation as f32);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // NOTE: This teapot was meant for OpenGL where the origin is at the lower left
 | 
					 | 
				
			||||||
        // instead the origin is at the upper left in Vulkan, so we reverse the Y axis.
 | 
					 | 
				
			||||||
        let aspect_ratio = renderer.aspect_ratio();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let proj = Mat4::perspective_rh_gl(std::f32::consts::FRAC_PI_2, aspect_ratio, 0.01, 100.0);
 | 
					 | 
				
			||||||
        let view = Mat4::look_at_rh(
 | 
					 | 
				
			||||||
            Vec3::new(0.4, 0.3, 1.0),
 | 
					 | 
				
			||||||
            Vec3::new(0.0, 0.0, 0.0),
 | 
					 | 
				
			||||||
            Vec3::new(0.0, -1.0, 0.0),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        let scale = Mat4::from_scale(Vec3::splat(0.01));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let uniform_data = vs::Data {
 | 
					 | 
				
			||||||
            world: Mat4::from_mat3(rotation).to_cols_array_2d(),
 | 
					 | 
				
			||||||
            view: (view * scale).to_cols_array_2d(),
 | 
					 | 
				
			||||||
            proj: proj.to_cols_array_2d(),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let subbuffer = uniform_buffer.allocate_sized().unwrap();
 | 
					 | 
				
			||||||
        *subbuffer.write().unwrap() = uniform_data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        subbuffer
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let mut builder = RecordingCommandBuffer::new(
 | 
					 | 
				
			||||||
        command_buffer_allocator.clone(),
 | 
					 | 
				
			||||||
        context.graphics_queue().queue_family_index(),
 | 
					 | 
				
			||||||
        CommandBufferLevel::Primary,
 | 
					 | 
				
			||||||
        CommandBufferBeginInfo {
 | 
					 | 
				
			||||||
            usage: CommandBufferUsage::OneTimeSubmit,
 | 
					 | 
				
			||||||
            ..Default::default()
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    .unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let layout = &pipeline.layout().set_layouts()[0];
 | 
					 | 
				
			||||||
    let set = DescriptorSet::new(
 | 
					 | 
				
			||||||
        descriptor_set_allocator.clone(),
 | 
					 | 
				
			||||||
        layout.clone(),
 | 
					 | 
				
			||||||
        [WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)],
 | 
					 | 
				
			||||||
        [],
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    .unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    builder
 | 
					 | 
				
			||||||
        .begin_render_pass(
 | 
					 | 
				
			||||||
            RenderPassBeginInfo {
 | 
					 | 
				
			||||||
                clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into()), Some(1f32.into())],
 | 
					 | 
				
			||||||
                ..RenderPassBeginInfo::framebuffer(
 | 
					 | 
				
			||||||
                    framebuffers[renderer.image_index() as usize].clone(),
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            Default::default(),
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        .unwrap()
 | 
					 | 
				
			||||||
        .bind_pipeline_graphics(pipeline.clone())
 | 
					 | 
				
			||||||
        .unwrap()
 | 
					 | 
				
			||||||
        .bind_descriptor_sets(
 | 
					 | 
				
			||||||
            PipelineBindPoint::Graphics,
 | 
					 | 
				
			||||||
            pipeline.layout().clone(),
 | 
					 | 
				
			||||||
            0,
 | 
					 | 
				
			||||||
            set,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        .unwrap()
 | 
					 | 
				
			||||||
        .bind_vertex_buffers(0, (vertex_buffer.clone(), normals_buffer.clone()))
 | 
					 | 
				
			||||||
        .unwrap()
 | 
					 | 
				
			||||||
        .bind_index_buffer(index_buffer.clone())
 | 
					 | 
				
			||||||
        .unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    unsafe {
 | 
					 | 
				
			||||||
        builder
 | 
					 | 
				
			||||||
            .draw_indexed(index_buffer.len() as u32, 1, 0, 0, 0)
 | 
					 | 
				
			||||||
            .unwrap();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    builder.end_render_pass(Default::default()).unwrap();
 | 
					 | 
				
			||||||
    // Finish recording the command buffer by calling `end`.
 | 
					 | 
				
			||||||
    let command_buffer = builder.end().unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    draw_gui(gui);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let before_future = previous_frame_end
 | 
					 | 
				
			||||||
        .then_execute(context.graphics_queue().clone(), command_buffer)
 | 
					 | 
				
			||||||
        .unwrap()
 | 
					 | 
				
			||||||
        .boxed();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let after_future = gui
 | 
					 | 
				
			||||||
        .draw_on_image(before_future, renderer.swapchain_image_view())
 | 
					 | 
				
			||||||
        .boxed();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // The color output is now expected to contain our triangle. But in order to
 | 
					 | 
				
			||||||
    // show it on the screen, we have to *present* the image by calling
 | 
					 | 
				
			||||||
    // `present` on the window renderer.
 | 
					 | 
				
			||||||
    //
 | 
					 | 
				
			||||||
    // This function does not actually present the image immediately. Instead it
 | 
					 | 
				
			||||||
    // submits a present command at the end of the queue. This means that it will
 | 
					 | 
				
			||||||
    // only be presented once the GPU has finished executing the command buffer
 | 
					 | 
				
			||||||
    // that draws the triangle.
 | 
					 | 
				
			||||||
    renderer.present(after_future, true);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn draw_gui(gui: &mut Gui) {
 | 
					 | 
				
			||||||
    let mut code = CODE.to_owned();
 | 
					 | 
				
			||||||
    gui.immediate_ui(|gui| {
 | 
					 | 
				
			||||||
        let ctx = gui.context();
 | 
					 | 
				
			||||||
        egui::Window::new("Colors").vscroll(true).show(&ctx, |ui| {
 | 
					 | 
				
			||||||
            ui.vertical_centered(|ui| {
 | 
					 | 
				
			||||||
                ui.add(egui::widgets::Label::new("Hi there!"));
 | 
					 | 
				
			||||||
                sized_text(ui, "Rich Text", 32.0);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            ui.separator();
 | 
					 | 
				
			||||||
            ui.columns(2, |columns| {
 | 
					 | 
				
			||||||
                egui::ScrollArea::vertical()
 | 
					 | 
				
			||||||
                    .id_source("source")
 | 
					 | 
				
			||||||
                    .show(&mut columns[0], |ui| {
 | 
					 | 
				
			||||||
                        ui.add(
 | 
					 | 
				
			||||||
                            egui::TextEdit::multiline(&mut code).font(egui::TextStyle::Monospace),
 | 
					 | 
				
			||||||
                        );
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                egui::ScrollArea::vertical()
 | 
					 | 
				
			||||||
                    .id_source("rendered")
 | 
					 | 
				
			||||||
                    .show(&mut columns[1], |ui| {
 | 
					 | 
				
			||||||
                        ui.add(egui::widgets::Label::new("Good day!"));
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn sized_text(ui: &mut egui::Ui, text: impl Into<String>, size: f32) {
 | 
					 | 
				
			||||||
    ui.label(
 | 
					 | 
				
			||||||
        egui::RichText::new(text)
 | 
					 | 
				
			||||||
            .size(size)
 | 
					 | 
				
			||||||
            .family(egui::FontFamily::Monospace),
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const CODE: &str = r"
 | 
					 | 
				
			||||||
# Some markup
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
let mut gui = Gui::new(&event_loop, renderer.surface(), None, renderer.queue(), SampleCount::Sample1);
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn window_size_dependent_setup(
 | 
					 | 
				
			||||||
    memory_allocator: Arc<StandardMemoryAllocator>,
 | 
					 | 
				
			||||||
    vs: EntryPoint,
 | 
					 | 
				
			||||||
    fs: EntryPoint,
 | 
					 | 
				
			||||||
    image_views: &[Arc<ImageView>],
 | 
					 | 
				
			||||||
    render_pass: Arc<RenderPass>,
 | 
					 | 
				
			||||||
) -> (Arc<GraphicsPipeline>, Vec<Arc<Framebuffer>>) {
 | 
					 | 
				
			||||||
    let device = memory_allocator.device().clone();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let extent = image_views[0].image().extent();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let depth_buffer = ImageView::new_default(
 | 
					 | 
				
			||||||
        Image::new(
 | 
					 | 
				
			||||||
            memory_allocator,
 | 
					 | 
				
			||||||
            ImageCreateInfo {
 | 
					 | 
				
			||||||
                image_type: ImageType::Dim2d,
 | 
					 | 
				
			||||||
                format: Format::D16_UNORM,
 | 
					 | 
				
			||||||
                extent,
 | 
					 | 
				
			||||||
                usage: ImageUsage::DEPTH_STENCIL_ATTACHMENT | ImageUsage::TRANSIENT_ATTACHMENT,
 | 
					 | 
				
			||||||
                ..Default::default()
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            AllocationCreateInfo::default(),
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        .unwrap(),
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    .unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let framebuffers = image_views
 | 
					 | 
				
			||||||
        .iter()
 | 
					 | 
				
			||||||
        .map(|image_view| {
 | 
					 | 
				
			||||||
            Framebuffer::new(
 | 
					 | 
				
			||||||
                render_pass.clone(),
 | 
					 | 
				
			||||||
                FramebufferCreateInfo {
 | 
					 | 
				
			||||||
                    attachments: vec![image_view.clone(), depth_buffer.clone()],
 | 
					 | 
				
			||||||
                    ..Default::default()
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            .unwrap()
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .collect::<Vec<_>>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // In the triangle example we use a dynamic viewport, as its a simple example. However in the
 | 
					 | 
				
			||||||
    // teapot example, we recreate the pipelines with a hardcoded viewport instead. This allows the
 | 
					 | 
				
			||||||
    // driver to optimize things, at the cost of slower window resizes.
 | 
					 | 
				
			||||||
    // https://computergraphics.stackexchange.com/questions/5742/vulkan-best-way-of-updating-pipeline-viewport
 | 
					 | 
				
			||||||
    let pipeline = {
 | 
					 | 
				
			||||||
        let vertex_input_state = [Position::per_vertex(), Normal::per_vertex()]
 | 
					 | 
				
			||||||
            .definition(&vs)
 | 
					 | 
				
			||||||
            .unwrap();
 | 
					 | 
				
			||||||
        let stages = [
 | 
					 | 
				
			||||||
            PipelineShaderStageCreateInfo::new(vs),
 | 
					 | 
				
			||||||
            PipelineShaderStageCreateInfo::new(fs),
 | 
					 | 
				
			||||||
        ];
 | 
					 | 
				
			||||||
        let layout = PipelineLayout::new(
 | 
					 | 
				
			||||||
            device.clone(),
 | 
					 | 
				
			||||||
            PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages)
 | 
					 | 
				
			||||||
                .into_pipeline_layout_create_info(device.clone())
 | 
					 | 
				
			||||||
                .unwrap(),
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        .unwrap();
 | 
					 | 
				
			||||||
        let subpass = Subpass::from(render_pass, 0).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        GraphicsPipeline::new(
 | 
					 | 
				
			||||||
            device,
 | 
					 | 
				
			||||||
            None,
 | 
					 | 
				
			||||||
            GraphicsPipelineCreateInfo {
 | 
					 | 
				
			||||||
                stages: stages.into_iter().collect(),
 | 
					 | 
				
			||||||
                vertex_input_state: Some(vertex_input_state),
 | 
					 | 
				
			||||||
                input_assembly_state: Some(InputAssemblyState::default()),
 | 
					 | 
				
			||||||
                viewport_state: Some(ViewportState {
 | 
					 | 
				
			||||||
                    viewports: [Viewport {
 | 
					 | 
				
			||||||
                        offset: [0.0, 0.0],
 | 
					 | 
				
			||||||
                        extent: [extent[0] as f32, extent[1] as f32],
 | 
					 | 
				
			||||||
                        depth_range: 0.0..=1.0,
 | 
					 | 
				
			||||||
                    }]
 | 
					 | 
				
			||||||
                    .into_iter()
 | 
					 | 
				
			||||||
                    .collect(),
 | 
					 | 
				
			||||||
                    ..Default::default()
 | 
					 | 
				
			||||||
                }),
 | 
					 | 
				
			||||||
                rasterization_state: Some(RasterizationState::default()),
 | 
					 | 
				
			||||||
                depth_stencil_state: Some(DepthStencilState {
 | 
					 | 
				
			||||||
                    depth: Some(DepthState::simple()),
 | 
					 | 
				
			||||||
                    ..Default::default()
 | 
					 | 
				
			||||||
                }),
 | 
					 | 
				
			||||||
                multisample_state: Some(MultisampleState::default()),
 | 
					 | 
				
			||||||
                color_blend_state: Some(ColorBlendState::with_attachment_states(
 | 
					 | 
				
			||||||
                    subpass.num_color_attachments(),
 | 
					 | 
				
			||||||
                    ColorBlendAttachmentState::default(),
 | 
					 | 
				
			||||||
                )),
 | 
					 | 
				
			||||||
                subpass: Some(subpass.into()),
 | 
					 | 
				
			||||||
                ..GraphicsPipelineCreateInfo::layout(layout)
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        .unwrap()
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    (pipeline, framebuffers)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
mod vs {
 | 
					 | 
				
			||||||
    vulkano_shaders::shader! {
 | 
					 | 
				
			||||||
        ty: "vertex",
 | 
					 | 
				
			||||||
        path: "src/vert.glsl",
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
mod fs {
 | 
					 | 
				
			||||||
    vulkano_shaders::shader! {
 | 
					 | 
				
			||||||
        ty: "fragment",
 | 
					 | 
				
			||||||
        path: "src/frag.glsl",
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1025
									
								
								modules/khors-graphics/src/node_editor/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1025
									
								
								modules/khors-graphics/src/node_editor/mod.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										504
									
								
								modules/khors-graphics/src/renderer.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										504
									
								
								modules/khors-graphics/src/renderer.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,504 @@
 | 
				
			||||||
 | 
					use flax::{entity_ids, BoxedSystem, Query, QueryBorrow, Schedule, System, World};
 | 
				
			||||||
 | 
					use glam::{
 | 
				
			||||||
 | 
					    f32::{Mat3, Vec3},
 | 
				
			||||||
 | 
					    Mat4,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					use vulkano::{
 | 
				
			||||||
 | 
					    buffer::{
 | 
				
			||||||
 | 
					        allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo},
 | 
				
			||||||
 | 
					        Buffer, BufferCreateInfo, BufferUsage,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    command_buffer::{
 | 
				
			||||||
 | 
					        allocator::{CommandBufferAllocator, StandardCommandBufferAllocator},
 | 
				
			||||||
 | 
					        CommandBufferBeginInfo, CommandBufferLevel, CommandBufferUsage, RecordingCommandBuffer,
 | 
				
			||||||
 | 
					        RenderPassBeginInfo,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    descriptor_set::{
 | 
				
			||||||
 | 
					        allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    device::DeviceOwned,
 | 
				
			||||||
 | 
					    format::Format,
 | 
				
			||||||
 | 
					    image::{view::ImageView, Image, ImageCreateInfo, ImageType, ImageUsage},
 | 
				
			||||||
 | 
					    memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
 | 
				
			||||||
 | 
					    pipeline::{graphics::viewport::Viewport, GraphicsPipeline, Pipeline, PipelineBindPoint},
 | 
				
			||||||
 | 
					    render_pass::{Framebuffer, RenderPass},
 | 
				
			||||||
 | 
					    shader::EntryPoint,
 | 
				
			||||||
 | 
					    sync::GpuFuture,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use vulkano_util::{
 | 
				
			||||||
 | 
					    context::VulkanoContext, renderer::VulkanoWindowRenderer, window::VulkanoWindows,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{node_editor, render_module::RenderModule as ThreadLocalModule, vulkan::renderpass::make_render_pass};
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    debug_gui::DebugGuiStack,
 | 
				
			||||||
 | 
					    vulkan::{
 | 
				
			||||||
 | 
					        framebuffer::make_framebuffer,
 | 
				
			||||||
 | 
					        pipeline::{make_pipeline, PassInfo, PipelineInfo},
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use egui_vulkano::Gui;
 | 
				
			||||||
 | 
					use super::node_editor::DemoApp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::temp::model::{INDICES, NORMALS, POSITIONS};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct RenderModule {
 | 
				
			||||||
 | 
					    schedule: Schedule,
 | 
				
			||||||
 | 
					    memory_allocator: Arc<StandardMemoryAllocator>,
 | 
				
			||||||
 | 
					    descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
 | 
				
			||||||
 | 
					    command_buffer_allocator: Arc<dyn CommandBufferAllocator>,
 | 
				
			||||||
 | 
					    viewport: Viewport,
 | 
				
			||||||
 | 
					    rotation_start: std::time::Instant,
 | 
				
			||||||
 | 
					    node_editor: DemoApp,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl RenderModule {
 | 
				
			||||||
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        vk_context: &mut VulkanoContext,
 | 
				
			||||||
 | 
					        _vk_windows: &mut VulkanoWindows,
 | 
				
			||||||
 | 
					        _schedule: &mut Schedule,
 | 
				
			||||||
 | 
					        _world: &mut World,
 | 
				
			||||||
 | 
					        _events: &mut khors_core::events::Events,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        let schedule = Schedule::builder()
 | 
				
			||||||
 | 
					            .with_system(add_distance_system())
 | 
				
			||||||
 | 
					            .build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(
 | 
				
			||||||
 | 
					            vk_context.device().clone(),
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
 | 
				
			||||||
 | 
					            vk_context.device().clone(),
 | 
				
			||||||
 | 
					            Default::default(),
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
 | 
				
			||||||
 | 
					            vk_context.device().clone(),
 | 
				
			||||||
 | 
					            Default::default(),
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let viewport = Viewport {
 | 
				
			||||||
 | 
					            offset: [0.0, 0.0],
 | 
				
			||||||
 | 
					            extent: [0.0, 0.0],
 | 
				
			||||||
 | 
					            depth_range: 0.0..=1.0,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let node_editor = DemoApp::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let rotation_start = std::time::Instant::now();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            schedule,
 | 
				
			||||||
 | 
					            memory_allocator,
 | 
				
			||||||
 | 
					            descriptor_set_allocator,
 | 
				
			||||||
 | 
					            command_buffer_allocator,
 | 
				
			||||||
 | 
					            viewport,
 | 
				
			||||||
 | 
					            rotation_start,
 | 
				
			||||||
 | 
					            node_editor,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ThreadLocalModule for RenderModule {
 | 
				
			||||||
 | 
					    fn on_update(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        gui_stack: &mut DebugGuiStack,
 | 
				
			||||||
 | 
					        vk_context: &mut VulkanoContext,
 | 
				
			||||||
 | 
					        vk_windows: &mut vulkano_util::window::VulkanoWindows,
 | 
				
			||||||
 | 
					        world: &mut World,
 | 
				
			||||||
 | 
					        _events: &mut khors_core::events::Events,
 | 
				
			||||||
 | 
					        _frame_time: std::time::Duration,
 | 
				
			||||||
 | 
					    ) -> anyhow::Result<()> {
 | 
				
			||||||
 | 
					        self.schedule.execute_seq(world).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let viewport = &mut self.viewport;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (window_id, renderer) in vk_windows.iter_mut() {
 | 
				
			||||||
 | 
					            let gui = gui_stack.get_mut(*window_id).unwrap();
 | 
				
			||||||
 | 
					            let node_editor = &mut self.node_editor;
 | 
				
			||||||
 | 
					            draw(
 | 
				
			||||||
 | 
					                vk_context.device().clone(),
 | 
				
			||||||
 | 
					                self.memory_allocator.clone(),
 | 
				
			||||||
 | 
					                self.descriptor_set_allocator.clone(),
 | 
				
			||||||
 | 
					                self.command_buffer_allocator.clone(),
 | 
				
			||||||
 | 
					                viewport,
 | 
				
			||||||
 | 
					                vk_context,
 | 
				
			||||||
 | 
					                renderer,
 | 
				
			||||||
 | 
					                gui,
 | 
				
			||||||
 | 
					                node_editor,
 | 
				
			||||||
 | 
					                self.rotation_start,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn add_distance_system() -> BoxedSystem {
 | 
				
			||||||
 | 
					    let query = Query::new(entity_ids());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    System::builder()
 | 
				
			||||||
 | 
					        .with_query(query)
 | 
				
			||||||
 | 
					        .build(|mut query: QueryBorrow<'_, flax::EntityIds, _>| {
 | 
				
			||||||
 | 
					            for _id in &mut query {
 | 
				
			||||||
 | 
					                // println!("----------: {}", _id.index());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .boxed()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[allow(clippy::too_many_arguments)]
 | 
				
			||||||
 | 
					fn draw(
 | 
				
			||||||
 | 
					    device: Arc<vulkano::device::Device>,
 | 
				
			||||||
 | 
					    memory_allocator: Arc<StandardMemoryAllocator>,
 | 
				
			||||||
 | 
					    descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
 | 
				
			||||||
 | 
					    command_buffer_allocator: Arc<dyn CommandBufferAllocator>,
 | 
				
			||||||
 | 
					    _viewport: &mut Viewport,
 | 
				
			||||||
 | 
					    context: &mut VulkanoContext,
 | 
				
			||||||
 | 
					    renderer: &mut VulkanoWindowRenderer,
 | 
				
			||||||
 | 
					    gui: &mut Gui,
 | 
				
			||||||
 | 
					    node_editor: &mut DemoApp,
 | 
				
			||||||
 | 
					    rotation_start: std::time::Instant,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    let vertex_buffer = Buffer::from_iter(
 | 
				
			||||||
 | 
					        memory_allocator.clone(),
 | 
				
			||||||
 | 
					        BufferCreateInfo {
 | 
				
			||||||
 | 
					            usage: BufferUsage::VERTEX_BUFFER,
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        AllocationCreateInfo {
 | 
				
			||||||
 | 
					            memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
 | 
				
			||||||
 | 
					                | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        POSITIONS,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .unwrap();
 | 
				
			||||||
 | 
					    let normals_buffer = Buffer::from_iter(
 | 
				
			||||||
 | 
					        memory_allocator.clone(),
 | 
				
			||||||
 | 
					        BufferCreateInfo {
 | 
				
			||||||
 | 
					            usage: BufferUsage::VERTEX_BUFFER,
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        AllocationCreateInfo {
 | 
				
			||||||
 | 
					            memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
 | 
				
			||||||
 | 
					                | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        NORMALS,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .unwrap();
 | 
				
			||||||
 | 
					    let index_buffer = Buffer::from_iter(
 | 
				
			||||||
 | 
					        memory_allocator.clone(),
 | 
				
			||||||
 | 
					        BufferCreateInfo {
 | 
				
			||||||
 | 
					            usage: BufferUsage::INDEX_BUFFER,
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        AllocationCreateInfo {
 | 
				
			||||||
 | 
					            memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
 | 
				
			||||||
 | 
					                | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        INDICES,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let uniform_buffer = SubbufferAllocator::new(
 | 
				
			||||||
 | 
					        memory_allocator.clone(),
 | 
				
			||||||
 | 
					        SubbufferAllocatorCreateInfo {
 | 
				
			||||||
 | 
					            buffer_usage: BufferUsage::UNIFORM_BUFFER,
 | 
				
			||||||
 | 
					            memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
 | 
				
			||||||
 | 
					                | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // let render_pass = vulkano::single_pass_renderpass!(
 | 
				
			||||||
 | 
					    //     device.clone(),
 | 
				
			||||||
 | 
					    //     attachments: {
 | 
				
			||||||
 | 
					    //         color: {
 | 
				
			||||||
 | 
					    //             format: renderer.swapchain_format(),
 | 
				
			||||||
 | 
					    //             samples: 1,
 | 
				
			||||||
 | 
					    //             load_op: Clear,
 | 
				
			||||||
 | 
					    //             store_op: Store,
 | 
				
			||||||
 | 
					    //         },
 | 
				
			||||||
 | 
					    //         depth_stencil: {
 | 
				
			||||||
 | 
					    //             format: Format::D16_UNORM,
 | 
				
			||||||
 | 
					    //             samples: 1,
 | 
				
			||||||
 | 
					    //             load_op: Clear,
 | 
				
			||||||
 | 
					    //             store_op: DontCare,
 | 
				
			||||||
 | 
					    //         },
 | 
				
			||||||
 | 
					    //     },
 | 
				
			||||||
 | 
					    //     pass: {
 | 
				
			||||||
 | 
					    //         color: [color],
 | 
				
			||||||
 | 
					    //         depth_stencil: {depth_stencil},
 | 
				
			||||||
 | 
					    //     },
 | 
				
			||||||
 | 
					    // )
 | 
				
			||||||
 | 
					    //     .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let render_pass = make_render_pass(device.clone(), renderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let vs = vs::load(device.clone())
 | 
				
			||||||
 | 
					        .unwrap()
 | 
				
			||||||
 | 
					        .entry_point("main")
 | 
				
			||||||
 | 
					        .unwrap();
 | 
				
			||||||
 | 
					    let fs = fs::load(device.clone())
 | 
				
			||||||
 | 
					        .unwrap()
 | 
				
			||||||
 | 
					        .entry_point("main")
 | 
				
			||||||
 | 
					        .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let (mut pipeline, mut framebuffers) = window_size_dependent_setup(
 | 
				
			||||||
 | 
					        memory_allocator.clone(),
 | 
				
			||||||
 | 
					        vs.clone(),
 | 
				
			||||||
 | 
					        fs.clone(),
 | 
				
			||||||
 | 
					        renderer.swapchain_image_views(),
 | 
				
			||||||
 | 
					        render_pass.clone(),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Do not draw the frame when the screen size is zero. On Windows, this can
 | 
				
			||||||
 | 
					    // occur when minimizing the application.
 | 
				
			||||||
 | 
					    let image_extent: [u32; 2] = renderer.window().inner_size().into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if image_extent.contains(&0) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Begin rendering by acquiring the gpu future from the window renderer.
 | 
				
			||||||
 | 
					    let previous_frame_end = renderer
 | 
				
			||||||
 | 
					        .acquire(None, |swapchain_images| {
 | 
				
			||||||
 | 
					            // Whenever the window resizes we need to recreate everything dependent
 | 
				
			||||||
 | 
					            // on the window size. In this example that
 | 
				
			||||||
 | 
					            // includes the swapchain, the framebuffers
 | 
				
			||||||
 | 
					            // and the dynamic state viewport.
 | 
				
			||||||
 | 
					            let (new_pipeline, new_framebuffers) = window_size_dependent_setup(
 | 
				
			||||||
 | 
					                memory_allocator.clone(),
 | 
				
			||||||
 | 
					                vs.clone(),
 | 
				
			||||||
 | 
					                fs.clone(),
 | 
				
			||||||
 | 
					                swapchain_images,
 | 
				
			||||||
 | 
					                render_pass.clone(),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            pipeline = new_pipeline;
 | 
				
			||||||
 | 
					            framebuffers = new_framebuffers;
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let uniform_buffer_subbuffer = {
 | 
				
			||||||
 | 
					        let elapsed = rotation_start.elapsed();
 | 
				
			||||||
 | 
					        let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0;
 | 
				
			||||||
 | 
					        let rotation = Mat3::from_rotation_y(rotation as f32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // NOTE: This teapot was meant for OpenGL where the origin is at the lower left
 | 
				
			||||||
 | 
					        // instead the origin is at the upper left in Vulkan, so we reverse the Y axis.
 | 
				
			||||||
 | 
					        let aspect_ratio = renderer.aspect_ratio();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let proj = Mat4::perspective_rh_gl(std::f32::consts::FRAC_PI_2, aspect_ratio, 0.01, 100.0);
 | 
				
			||||||
 | 
					        let view = Mat4::look_at_rh(
 | 
				
			||||||
 | 
					            Vec3::new(0.4, 0.3, 1.0),
 | 
				
			||||||
 | 
					            Vec3::new(0.0, 0.0, 0.0),
 | 
				
			||||||
 | 
					            Vec3::new(0.0, -1.0, 0.0),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        let scale = Mat4::from_scale(Vec3::splat(0.01));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let uniform_data = vs::Data {
 | 
				
			||||||
 | 
					            world: Mat4::from_mat3(rotation).to_cols_array_2d(),
 | 
				
			||||||
 | 
					            view: (view * scale).to_cols_array_2d(),
 | 
				
			||||||
 | 
					            proj: proj.to_cols_array_2d(),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let subbuffer = uniform_buffer.allocate_sized().unwrap();
 | 
				
			||||||
 | 
					        *subbuffer.write().unwrap() = uniform_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        subbuffer
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut builder = RecordingCommandBuffer::new(
 | 
				
			||||||
 | 
					        command_buffer_allocator.clone(),
 | 
				
			||||||
 | 
					        context.graphics_queue().queue_family_index(),
 | 
				
			||||||
 | 
					        CommandBufferLevel::Primary,
 | 
				
			||||||
 | 
					        CommandBufferBeginInfo {
 | 
				
			||||||
 | 
					            usage: CommandBufferUsage::OneTimeSubmit,
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let layout = &pipeline.layout().set_layouts()[0];
 | 
				
			||||||
 | 
					    let set = DescriptorSet::new(
 | 
				
			||||||
 | 
					        descriptor_set_allocator.clone(),
 | 
				
			||||||
 | 
					        layout.clone(),
 | 
				
			||||||
 | 
					        [WriteDescriptorSet::buffer(0, uniform_buffer_subbuffer)],
 | 
				
			||||||
 | 
					        [],
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    builder
 | 
				
			||||||
 | 
					        .begin_render_pass(
 | 
				
			||||||
 | 
					            RenderPassBeginInfo {
 | 
				
			||||||
 | 
					                clear_values: vec![Some([0.0, 0.0, 1.0, 1.0].into()), Some(1f32.into())],
 | 
				
			||||||
 | 
					                ..RenderPassBeginInfo::framebuffer(
 | 
				
			||||||
 | 
					                    framebuffers[renderer.image_index() as usize].clone(),
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Default::default(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .unwrap()
 | 
				
			||||||
 | 
					        .bind_pipeline_graphics(pipeline.clone())
 | 
				
			||||||
 | 
					        .unwrap()
 | 
				
			||||||
 | 
					        .bind_descriptor_sets(
 | 
				
			||||||
 | 
					            PipelineBindPoint::Graphics,
 | 
				
			||||||
 | 
					            pipeline.layout().clone(),
 | 
				
			||||||
 | 
					            0,
 | 
				
			||||||
 | 
					            set,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .unwrap()
 | 
				
			||||||
 | 
					        .bind_vertex_buffers(0, (vertex_buffer.clone(), normals_buffer.clone()))
 | 
				
			||||||
 | 
					        .unwrap()
 | 
				
			||||||
 | 
					        .bind_index_buffer(index_buffer.clone())
 | 
				
			||||||
 | 
					        .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unsafe {
 | 
				
			||||||
 | 
					        builder
 | 
				
			||||||
 | 
					            .draw_indexed(index_buffer.len() as u32, 1, 0, 0, 0)
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    builder.end_render_pass(Default::default()).unwrap();
 | 
				
			||||||
 | 
					    // Finish recording the command buffer by calling `end`.
 | 
				
			||||||
 | 
					    let command_buffer = builder.end().unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    draw_gui(gui, node_editor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let before_future = previous_frame_end
 | 
				
			||||||
 | 
					        .then_execute(context.graphics_queue().clone(), command_buffer)
 | 
				
			||||||
 | 
					        .unwrap()
 | 
				
			||||||
 | 
					        .boxed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let after_future = gui
 | 
				
			||||||
 | 
					        .draw_on_image(before_future, renderer.swapchain_image_view())
 | 
				
			||||||
 | 
					        .boxed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // The color output is now expected to contain our triangle. But in order to
 | 
				
			||||||
 | 
					    // show it on the screen, we have to *present* the image by calling
 | 
				
			||||||
 | 
					    // `present` on the window renderer.
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    // This function does not actually present the image immediately. Instead it
 | 
				
			||||||
 | 
					    // submits a present command at the end of the queue. This means that it will
 | 
				
			||||||
 | 
					    // only be presented once the GPU has finished executing the command buffer
 | 
				
			||||||
 | 
					    // that draws the triangle.
 | 
				
			||||||
 | 
					    renderer.present(after_future, true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn draw_gui(gui: &mut Gui, node_editor: &mut DemoApp) {
 | 
				
			||||||
 | 
					    let mut code = CODE.to_owned();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    gui.immediate_ui(|gui| {
 | 
				
			||||||
 | 
					        let ctx = gui.context();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        node_editor.update(&ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        egui::Window::new("Colors").vscroll(true).show(&ctx, |ui| {
 | 
				
			||||||
 | 
					            ui.vertical_centered(|ui| {
 | 
				
			||||||
 | 
					                ui.add(egui::widgets::Label::new("Hi there!"));
 | 
				
			||||||
 | 
					                sized_text(ui, "Rich Text", 32.0);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            ui.separator();
 | 
				
			||||||
 | 
					            ui.columns(2, |columns| {
 | 
				
			||||||
 | 
					                egui::ScrollArea::vertical()
 | 
				
			||||||
 | 
					                    .id_source("source")
 | 
				
			||||||
 | 
					                    .show(&mut columns[0], |ui| {
 | 
				
			||||||
 | 
					                        ui.add(
 | 
				
			||||||
 | 
					                            egui::TextEdit::multiline(&mut code).font(egui::TextStyle::Monospace),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                egui::ScrollArea::vertical()
 | 
				
			||||||
 | 
					                    .id_source("rendered")
 | 
				
			||||||
 | 
					                    .show(&mut columns[1], |ui| {
 | 
				
			||||||
 | 
					                        ui.add(egui::widgets::Label::new("Good day!"));
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn sized_text(ui: &mut egui::Ui, text: impl Into<String>, size: f32) {
 | 
				
			||||||
 | 
					    ui.label(
 | 
				
			||||||
 | 
					        egui::RichText::new(text)
 | 
				
			||||||
 | 
					            .size(size)
 | 
				
			||||||
 | 
					            .family(egui::FontFamily::Monospace),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const CODE: &str = r"
 | 
				
			||||||
 | 
					# Some markup
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					let mut gui = Gui::new(&event_loop, renderer.surface(), None, renderer.queue(), SampleCount::Sample1);
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn window_size_dependent_setup(
 | 
				
			||||||
 | 
					    memory_allocator: Arc<StandardMemoryAllocator>,
 | 
				
			||||||
 | 
					    vs: EntryPoint,
 | 
				
			||||||
 | 
					    fs: EntryPoint,
 | 
				
			||||||
 | 
					    image_views: &[Arc<ImageView>],
 | 
				
			||||||
 | 
					    render_pass: Arc<RenderPass>,
 | 
				
			||||||
 | 
					) -> (Arc<GraphicsPipeline>, Vec<Arc<Framebuffer>>) {
 | 
				
			||||||
 | 
					    let device = memory_allocator.device().clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let extent = image_views[0].image().extent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let depth_buffer = ImageView::new_default(
 | 
				
			||||||
 | 
					        Image::new(
 | 
				
			||||||
 | 
					            memory_allocator,
 | 
				
			||||||
 | 
					            ImageCreateInfo {
 | 
				
			||||||
 | 
					                image_type: ImageType::Dim2d,
 | 
				
			||||||
 | 
					                format: Format::D16_UNORM,
 | 
				
			||||||
 | 
					                extent,
 | 
				
			||||||
 | 
					                usage: ImageUsage::DEPTH_STENCIL_ATTACHMENT | ImageUsage::TRANSIENT_ATTACHMENT,
 | 
				
			||||||
 | 
					                ..Default::default()
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            AllocationCreateInfo::default(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .unwrap(),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let framebuffers = image_views
 | 
				
			||||||
 | 
					        .iter()
 | 
				
			||||||
 | 
					        .map(|image_view| {
 | 
				
			||||||
 | 
					            make_framebuffer(
 | 
				
			||||||
 | 
					                render_pass.clone(),
 | 
				
			||||||
 | 
					                vec![image_view.clone(), depth_buffer.clone()],
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .unwrap()
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // In the triangle example we use a dynamic viewport, as its a simple example. However in the
 | 
				
			||||||
 | 
					    // teapot example, we recreate the pipelines with a hardcoded viewport instead. This allows the
 | 
				
			||||||
 | 
					    // driver to optimize things, at the cost of slower window resizes.
 | 
				
			||||||
 | 
					    // https://computergraphics.stackexchange.com/questions/5742/vulkan-best-way-of-updating-pipeline-viewport
 | 
				
			||||||
 | 
					    let pipeline_info = PipelineInfo { vs, fs };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let pass_info = PassInfo::new(render_pass.clone(), 0, extent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let pipeline = make_pipeline(device.clone(), &pipeline_info, &pass_info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (pipeline, framebuffers)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod vs {
 | 
				
			||||||
 | 
					    vulkano_shaders::shader! {
 | 
				
			||||||
 | 
					        ty: "vertex",
 | 
				
			||||||
 | 
					        path: "src/temp/vert.glsl",
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod fs {
 | 
				
			||||||
 | 
					    vulkano_shaders::shader! {
 | 
				
			||||||
 | 
					        ty: "fragment",
 | 
				
			||||||
 | 
					        path: "src/temp/frag.glsl",
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										33
									
								
								modules/khors-graphics/src/rendergraph/error.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								modules/khors-graphics/src/rendergraph/error.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					use thiserror::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::{node::NodeKind, rendergraph::{NodeIndex, ResourceKind}};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub type Result<T> = std::result::Result<T, Error>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Error)]
 | 
				
			||||||
 | 
					pub enum Error {
 | 
				
			||||||
 | 
					    #[error("Failed executing rendergraph node")]
 | 
				
			||||||
 | 
					    NodeExecution(#[from] anyhow::Error),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[error("Rendergraph vulkan error")]
 | 
				
			||||||
 | 
					    Vulkan(#[from] ivy_vulkan::Error),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[error("Rendergraph graphics error")]
 | 
				
			||||||
 | 
					    Graphics(#[from] ivy_graphics::Error),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[error("Dependency cycle in rendergraph")]
 | 
				
			||||||
 | 
					    DependencyCycle,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[error("Node read attachment is missing corresponding write attachment for {2:?} required by node {0:?}: {1:?}")]
 | 
				
			||||||
 | 
					    MissingWrite(NodeIndex, &'static str, ResourceKind),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[error("Resource acquisition error")]
 | 
				
			||||||
 | 
					    Resource(#[from] ivy_resources::Error),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[error("Invalid node index {0:?}")]
 | 
				
			||||||
 | 
					    InvalidNodeIndex(NodeIndex),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[error("Specified node {0:?} is not the correct kind. Expected {1:?}, found {2:?}")]
 | 
				
			||||||
 | 
					    InvalidNodeKind(NodeIndex, NodeKind, NodeKind),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								modules/khors-graphics/src/rendergraph/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								modules/khors-graphics/src/rendergraph/mod.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					// use flax::component;
 | 
				
			||||||
 | 
					// use vulkano::image::{view::ImageView, Image};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// pub mod node;
 | 
				
			||||||
 | 
					// pub mod pass;
 | 
				
			||||||
 | 
					// pub mod rendergraph;
 | 
				
			||||||
 | 
					// mod error;
 | 
				
			||||||
							
								
								
									
										172
									
								
								modules/khors-graphics/src/rendergraph/node.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								modules/khors-graphics/src/rendergraph/node.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,172 @@
 | 
				
			||||||
 | 
					use flax::World;
 | 
				
			||||||
 | 
					use vulkano::command_buffer::RecordingCommandBuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::vulkan::pipeline::PassInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Represents a node in the renderpass.
 | 
				
			||||||
 | 
					pub trait Node: 'static + Send {
 | 
				
			||||||
 | 
					    /// Returns the color attachments for this node. Should not be execution heavy function
 | 
				
			||||||
 | 
					    fn color_attachments(&self) -> &[AttachmentInfo] {
 | 
				
			||||||
 | 
					        &[]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn output_attachments(&self) -> &[Handle<Texture>] {
 | 
				
			||||||
 | 
					        &[]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /// Returns the read attachments for this node. Should not be execution heavy function
 | 
				
			||||||
 | 
					    fn read_attachments(&self) -> &[Handle<Texture>] {
 | 
				
			||||||
 | 
					        &[]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /// Partially sampled input attachments. Read from the same pixel coord we write to
 | 
				
			||||||
 | 
					    fn input_attachments(&self) -> &[Handle<Texture>] {
 | 
				
			||||||
 | 
					        &[]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /// Returns the optional depth attachment for this node. Should not be execution heavy function
 | 
				
			||||||
 | 
					    fn depth_attachment(&self) -> Option<&AttachmentInfo> {
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn buffer_reads(&self) -> &[Buffer] {
 | 
				
			||||||
 | 
					        &[]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn buffer_writes(&self) -> &[Buffer] {
 | 
				
			||||||
 | 
					        &[]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the clear values to initiate this renderpass
 | 
				
			||||||
 | 
					    fn clear_values(&self) -> &[ClearValue] {
 | 
				
			||||||
 | 
					        &[]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn node_kind(&self) -> NodeKind;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Optional name, can be empty string
 | 
				
			||||||
 | 
					    fn debug_name(&self) -> &'static str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Execute this node inside a compatible renderpass
 | 
				
			||||||
 | 
					    fn execute(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        world: &mut World,
 | 
				
			||||||
 | 
					        resources: &Resources,
 | 
				
			||||||
 | 
					        cmd: &CommandBuffer,
 | 
				
			||||||
 | 
					        pass_info: &PassInfo,
 | 
				
			||||||
 | 
					        current_frame: usize,
 | 
				
			||||||
 | 
					    ) -> anyhow::Result<()>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | 
				
			||||||
 | 
					pub enum NodeKind {
 | 
				
			||||||
 | 
					    // A graphics rendering node. Renderpass and framebuffer will automatically be created.
 | 
				
			||||||
 | 
					    Graphics,
 | 
				
			||||||
 | 
					    // execution
 | 
				
			||||||
 | 
					    // A node that will be executed on the transfer queue. Appropriate pipeline barriers will
 | 
				
			||||||
 | 
					    // be inserted
 | 
				
			||||||
 | 
					    Transfer,
 | 
				
			||||||
 | 
					    // Compute,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone)]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
 | 
				
			||||||
 | 
					pub struct AttachmentInfo {
 | 
				
			||||||
 | 
					    // TODO, derive from edges
 | 
				
			||||||
 | 
					    pub store_op: StoreOp,
 | 
				
			||||||
 | 
					    pub load_op: LoadOp,
 | 
				
			||||||
 | 
					    pub initial_layout: ImageLayout,
 | 
				
			||||||
 | 
					    pub final_layout: ImageLayout,
 | 
				
			||||||
 | 
					    pub resource: Handle<Texture>,
 | 
				
			||||||
 | 
					    pub clear_value: ClearValue,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for AttachmentInfo {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            store_op: StoreOp::STORE,
 | 
				
			||||||
 | 
					            load_op: LoadOp::DONT_CARE,
 | 
				
			||||||
 | 
					            initial_layout: ImageLayout::UNDEFINED,
 | 
				
			||||||
 | 
					            final_layout: ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
 | 
				
			||||||
 | 
					            resource: Handle::null(),
 | 
				
			||||||
 | 
					            clear_value: ClearValue::default(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl AttachmentInfo {
 | 
				
			||||||
 | 
					    pub fn color(resource: Handle<Texture>) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            store_op: StoreOp::STORE,
 | 
				
			||||||
 | 
					            load_op: LoadOp::CLEAR,
 | 
				
			||||||
 | 
					            initial_layout: ImageLayout::UNDEFINED,
 | 
				
			||||||
 | 
					            final_layout: ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
 | 
				
			||||||
 | 
					            clear_value: ClearValue::color(0.0, 0.0, 0.0, 1.0),
 | 
				
			||||||
 | 
					            resource,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn depth_discard(resource: Handle<Texture>) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            store_op: StoreOp::DONT_CARE,
 | 
				
			||||||
 | 
					            load_op: LoadOp::CLEAR,
 | 
				
			||||||
 | 
					            initial_layout: ImageLayout::UNDEFINED,
 | 
				
			||||||
 | 
					            final_layout: ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
 | 
				
			||||||
 | 
					            clear_value: ClearValue::depth_stencil(1.0, 0),
 | 
				
			||||||
 | 
					            resource,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pub fn depth_store(resource: Handle<Texture>) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            store_op: StoreOp::STORE,
 | 
				
			||||||
 | 
					            load_op: LoadOp::CLEAR,
 | 
				
			||||||
 | 
					            initial_layout: ImageLayout::UNDEFINED,
 | 
				
			||||||
 | 
					            final_layout: ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
 | 
				
			||||||
 | 
					            clear_value: ClearValue::depth_stencil(1.0, 0),
 | 
				
			||||||
 | 
					            resource,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Simple node for rendering a pass in the rendergraph
 | 
				
			||||||
 | 
					pub struct RenderNode<Pass, T> {
 | 
				
			||||||
 | 
					    renderer: Handle<T>,
 | 
				
			||||||
 | 
					    marker: PhantomData<Pass>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<Pass, T> RenderNode<Pass, T> {
 | 
				
			||||||
 | 
					    pub fn new(renderer: Handle<T>) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            renderer,
 | 
				
			||||||
 | 
					            marker: PhantomData,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<Pass, T, E> Node for RenderNode<Pass, T>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    Pass: ShaderPass,
 | 
				
			||||||
 | 
					    T: 'static + Renderer<Error = E> + Send + Sync,
 | 
				
			||||||
 | 
					    E: Into<anyhow::Error>,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    fn node_kind(&self) -> NodeKind {
 | 
				
			||||||
 | 
					        NodeKind::Graphics
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn execute(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        world: &mut World,
 | 
				
			||||||
 | 
					        resources: &Resources,
 | 
				
			||||||
 | 
					        cmd: &CommandBuffer,
 | 
				
			||||||
 | 
					        pass_info: &PassInfo,
 | 
				
			||||||
 | 
					        current_frame: usize,
 | 
				
			||||||
 | 
					    ) -> anyhow::Result<()> {
 | 
				
			||||||
 | 
					        resources
 | 
				
			||||||
 | 
					            .get_mut(self.renderer)
 | 
				
			||||||
 | 
					            .with_context(|| format!("Failed to borrow {:?} mutably", type_name::<T>()))?
 | 
				
			||||||
 | 
					            .draw::<Pass>(world, resources, cmd, &[], pass_info, &[], current_frame)
 | 
				
			||||||
 | 
					            .map_err(|e| e.into())
 | 
				
			||||||
 | 
					            .with_context(|| format!("Failed to draw using {:?}", type_name::<T>()))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn debug_name(&self) -> &'static str {
 | 
				
			||||||
 | 
					        std::any::type_name::<RenderNode<Pass, T>>()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										395
									
								
								modules/khors-graphics/src/rendergraph/pass.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										395
									
								
								modules/khors-graphics/src/rendergraph/pass.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,395 @@
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use vulkano::{render_pass::Framebuffer, sync::ImageMemoryBarrier};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::{
 | 
				
			||||||
 | 
					    node::{Node, NodeKind},
 | 
				
			||||||
 | 
					    rendergraph::{Edge, NodeIndex, ResourceKind},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Pass {
 | 
				
			||||||
 | 
					    kind: PassKind,
 | 
				
			||||||
 | 
					    nodes: Vec<NodeIndex>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Pass {
 | 
				
			||||||
 | 
					    // Creates a new pass from a group of compatible nodes.
 | 
				
			||||||
 | 
					    // Two nodes are compatible if:
 | 
				
			||||||
 | 
					    // * They have no full read dependencies to another.
 | 
				
			||||||
 | 
					    // * Belong to the same kind and queue.
 | 
				
			||||||
 | 
					    // * Have the same dependency level.
 | 
				
			||||||
 | 
					    pub fn new<T>(
 | 
				
			||||||
 | 
					        context: &SharedVulkanContext,
 | 
				
			||||||
 | 
					        nodes: &Vec<Box<dyn Node>>,
 | 
				
			||||||
 | 
					        textures: &T,
 | 
				
			||||||
 | 
					        dependencies: &HashMap<NodeIndex, Vec<Edge>>,
 | 
				
			||||||
 | 
					        pass_nodes: Vec<NodeIndex>,
 | 
				
			||||||
 | 
					        kind: NodeKind,
 | 
				
			||||||
 | 
					        extent: Extent,
 | 
				
			||||||
 | 
					    ) -> Result<Self>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        T: Deref<Target = ResourceCache<Texture>>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let kind = match kind {
 | 
				
			||||||
 | 
					            NodeKind::Graphics => {
 | 
				
			||||||
 | 
					                PassKind::graphics(context, nodes, textures, dependencies, &pass_nodes, extent)?
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            NodeKind::Transfer => {
 | 
				
			||||||
 | 
					                PassKind::transfer(context, nodes, textures, dependencies, &pass_nodes, extent)?
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(Self {
 | 
				
			||||||
 | 
					            kind,
 | 
				
			||||||
 | 
					            nodes: pass_nodes,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn execute(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        world: &mut World,
 | 
				
			||||||
 | 
					        cmd: &CommandBuffer,
 | 
				
			||||||
 | 
					        nodes: &mut Vec<Box<dyn Node>>,
 | 
				
			||||||
 | 
					        current_frame: usize,
 | 
				
			||||||
 | 
					        extent: Extent,
 | 
				
			||||||
 | 
					    ) -> Result<()> {
 | 
				
			||||||
 | 
					        match &self.kind {
 | 
				
			||||||
 | 
					            PassKind::Graphics {
 | 
				
			||||||
 | 
					                renderpass,
 | 
				
			||||||
 | 
					                framebuffer,
 | 
				
			||||||
 | 
					                clear_values,
 | 
				
			||||||
 | 
					            } => {
 | 
				
			||||||
 | 
					                cmd.begin_renderpass(&renderpass, &framebuffer, extent, clear_values);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                self.nodes
 | 
				
			||||||
 | 
					                    .iter()
 | 
				
			||||||
 | 
					                    .enumerate()
 | 
				
			||||||
 | 
					                    .try_for_each(|(subpass, index)| -> Result<_> {
 | 
				
			||||||
 | 
					                        if subpass > 0 {
 | 
				
			||||||
 | 
					                            cmd.next_subpass(vk::SubpassContents::INLINE);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        let node = &mut nodes[*index];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        node.execute(
 | 
				
			||||||
 | 
					                            world,
 | 
				
			||||||
 | 
					                            resources,
 | 
				
			||||||
 | 
					                            cmd,
 | 
				
			||||||
 | 
					                            &PassInfo {
 | 
				
			||||||
 | 
					                                renderpass: renderpass.renderpass(),
 | 
				
			||||||
 | 
					                                subpass: subpass as u32,
 | 
				
			||||||
 | 
					                                extent,
 | 
				
			||||||
 | 
					                                color_attachment_count: node.color_attachments().len() as u32,
 | 
				
			||||||
 | 
					                                depth_attachment: node.depth_attachment().is_some(),
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                            current_frame,
 | 
				
			||||||
 | 
					                        )?;
 | 
				
			||||||
 | 
					                        Ok(())
 | 
				
			||||||
 | 
					                    })?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                cmd.end_renderpass();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            PassKind::Transfer {
 | 
				
			||||||
 | 
					                src_stage,
 | 
				
			||||||
 | 
					                image_barriers,
 | 
				
			||||||
 | 
					            } => {
 | 
				
			||||||
 | 
					                if !image_barriers.is_empty() {
 | 
				
			||||||
 | 
					                    cmd.pipeline_barrier(
 | 
				
			||||||
 | 
					                        *src_stage,
 | 
				
			||||||
 | 
					                        vk::PipelineStageFlags::TRANSFER,
 | 
				
			||||||
 | 
					                        &[],
 | 
				
			||||||
 | 
					                        image_barriers,
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                self.nodes.iter().try_for_each(|index| -> Result<_> {
 | 
				
			||||||
 | 
					                    nodes[*index]
 | 
				
			||||||
 | 
					                        .execute(world, resources, cmd, &PassInfo::default(), current_frame)
 | 
				
			||||||
 | 
					                        .map_err(|e| e.into())
 | 
				
			||||||
 | 
					                })?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get a reference to the pass's kind.
 | 
				
			||||||
 | 
					    pub fn kind(&self) -> &PassKind {
 | 
				
			||||||
 | 
					        &self.kind
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get a reference to the pass's nodes.
 | 
				
			||||||
 | 
					    pub fn nodes(&self) -> &[NodeIndex] {
 | 
				
			||||||
 | 
					        self.nodes.as_slice()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub enum PassKind {
 | 
				
			||||||
 | 
					    Graphics {
 | 
				
			||||||
 | 
					        renderpass: RenderPass,
 | 
				
			||||||
 | 
					        framebuffer: Framebuffer,
 | 
				
			||||||
 | 
					        clear_values: Vec<vk::ClearValue>,
 | 
				
			||||||
 | 
					        // Index into first node of pass_nodes
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Transfer {
 | 
				
			||||||
 | 
					        src_stage: vk::PipelineStageFlags,
 | 
				
			||||||
 | 
					        image_barriers: Vec<ImageMemoryBarrier>,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsafe impl Send for PassKind {}
 | 
				
			||||||
 | 
					unsafe impl Sync for PassKind {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl PassKind {
 | 
				
			||||||
 | 
					    fn graphics<T>(
 | 
				
			||||||
 | 
					        context: &SharedVulkanContext,
 | 
				
			||||||
 | 
					        nodes: &Vec<Box<dyn Node>>,
 | 
				
			||||||
 | 
					        textures: &T,
 | 
				
			||||||
 | 
					        dependencies: &HashMap<NodeIndex, Vec<Edge>>,
 | 
				
			||||||
 | 
					        pass_nodes: &[NodeIndex],
 | 
				
			||||||
 | 
					        extent: Extent,
 | 
				
			||||||
 | 
					    ) -> Result<Self>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        T: Deref<Target = ResourceCache<Texture>>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        println!(
 | 
				
			||||||
 | 
					            "Building pass with nodes: {:?}",
 | 
				
			||||||
 | 
					            pass_nodes
 | 
				
			||||||
 | 
					                .iter()
 | 
				
			||||||
 | 
					                .map(|v| nodes[*v].debug_name())
 | 
				
			||||||
 | 
					                .collect_vec()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Collect clear values
 | 
				
			||||||
 | 
					        let clear_values = pass_nodes
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .flat_map(|node| {
 | 
				
			||||||
 | 
					                let node = &nodes[*node];
 | 
				
			||||||
 | 
					                node.clear_values()
 | 
				
			||||||
 | 
					                    .iter()
 | 
				
			||||||
 | 
					                    .cloned()
 | 
				
			||||||
 | 
					                    .chain(repeat(ClearValue::default()).take(
 | 
				
			||||||
 | 
					                        node.color_attachments().len() + node.depth_attachment().iter().count()
 | 
				
			||||||
 | 
					                            - node.clear_values().len(),
 | 
				
			||||||
 | 
					                    ))
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Generate subpass dependencies
 | 
				
			||||||
 | 
					        let dependencies = pass_nodes
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .enumerate()
 | 
				
			||||||
 | 
					            .flat_map(|(subpass_index, node_index)| {
 | 
				
			||||||
 | 
					                // Get the dependencies of node.
 | 
				
			||||||
 | 
					                dependencies
 | 
				
			||||||
 | 
					                    .get(node_index)
 | 
				
			||||||
 | 
					                    .into_iter()
 | 
				
			||||||
 | 
					                    .flat_map(|val| val.iter())
 | 
				
			||||||
 | 
					                    .flat_map(move |edge| match edge.kind {
 | 
				
			||||||
 | 
					                        EdgeKind::Sampled => Some(SubpassDependency {
 | 
				
			||||||
 | 
					                            src_subpass: vk::SUBPASS_EXTERNAL,
 | 
				
			||||||
 | 
					                            dst_subpass: subpass_index as u32,
 | 
				
			||||||
 | 
					                            src_stage_mask: edge.write_stage,
 | 
				
			||||||
 | 
					                            dst_stage_mask: edge.read_stage,
 | 
				
			||||||
 | 
					                            src_access_mask: edge.write_access,
 | 
				
			||||||
 | 
					                            dst_access_mask: edge.read_access,
 | 
				
			||||||
 | 
					                            dependency_flags: Default::default(),
 | 
				
			||||||
 | 
					                        }),
 | 
				
			||||||
 | 
					                        EdgeKind::Input => Some(SubpassDependency {
 | 
				
			||||||
 | 
					                            src_subpass: pass_nodes
 | 
				
			||||||
 | 
					                                .iter()
 | 
				
			||||||
 | 
					                                .enumerate()
 | 
				
			||||||
 | 
					                                .find(|(_, node)| **node == edge.src)
 | 
				
			||||||
 | 
					                                .unwrap()
 | 
				
			||||||
 | 
					                                .0 as u32,
 | 
				
			||||||
 | 
					                            dst_subpass: subpass_index as u32,
 | 
				
			||||||
 | 
					                            src_stage_mask: edge.write_stage,
 | 
				
			||||||
 | 
					                            dst_stage_mask: edge.read_stage,
 | 
				
			||||||
 | 
					                            src_access_mask: edge.write_access,
 | 
				
			||||||
 | 
					                            dst_access_mask: edge.read_access,
 | 
				
			||||||
 | 
					                            dependency_flags: vk::DependencyFlags::BY_REGION,
 | 
				
			||||||
 | 
					                        }),
 | 
				
			||||||
 | 
					                        EdgeKind::Attachment => Some(SubpassDependency {
 | 
				
			||||||
 | 
					                            src_subpass: pass_nodes
 | 
				
			||||||
 | 
					                                .iter()
 | 
				
			||||||
 | 
					                                .enumerate()
 | 
				
			||||||
 | 
					                                .find(|(_, node)| **node == edge.src)
 | 
				
			||||||
 | 
					                                .map(|v| v.0 as u32)
 | 
				
			||||||
 | 
					                                .unwrap_or(vk::SUBPASS_EXTERNAL),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            dst_subpass: subpass_index as u32,
 | 
				
			||||||
 | 
					                            src_stage_mask: edge.write_stage,
 | 
				
			||||||
 | 
					                            dst_stage_mask: edge.read_stage,
 | 
				
			||||||
 | 
					                            src_access_mask: edge.write_access,
 | 
				
			||||||
 | 
					                            dst_access_mask: edge.read_access,
 | 
				
			||||||
 | 
					                            dependency_flags: vk::DependencyFlags::BY_REGION,
 | 
				
			||||||
 | 
					                        }),
 | 
				
			||||||
 | 
					                        EdgeKind::Buffer => None,
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut attachment_descriptions = Vec::new();
 | 
				
			||||||
 | 
					        let mut attachments = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let attachment_refs = pass_nodes
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .enumerate()
 | 
				
			||||||
 | 
					            .map(|(_, node_index)| -> Result<_> {
 | 
				
			||||||
 | 
					                let node = &nodes[*node_index];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let offset = attachments.len();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let color_attachments = node
 | 
				
			||||||
 | 
					                    .color_attachments()
 | 
				
			||||||
 | 
					                    .iter()
 | 
				
			||||||
 | 
					                    .enumerate()
 | 
				
			||||||
 | 
					                    .map(|(i, _)| AttachmentReference {
 | 
				
			||||||
 | 
					                        attachment: (i + offset) as u32,
 | 
				
			||||||
 | 
					                        layout: ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    .collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let input_attachments = node
 | 
				
			||||||
 | 
					                    .input_attachments()
 | 
				
			||||||
 | 
					                    .iter()
 | 
				
			||||||
 | 
					                    .map(|tex| -> Result<_> {
 | 
				
			||||||
 | 
					                        let view = textures.get(*tex)?.image_view();
 | 
				
			||||||
 | 
					                        Ok(AttachmentReference {
 | 
				
			||||||
 | 
					                            attachment: attachments
 | 
				
			||||||
 | 
					                                .iter()
 | 
				
			||||||
 | 
					                                .enumerate()
 | 
				
			||||||
 | 
					                                .find(|(_, val)| view == **val)
 | 
				
			||||||
 | 
					                                .unwrap()
 | 
				
			||||||
 | 
					                                .0 as u32,
 | 
				
			||||||
 | 
					                            layout: ImageLayout::SHADER_READ_ONLY_OPTIMAL,
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    .collect::<Result<Vec<_>>>()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let depth_attachment =
 | 
				
			||||||
 | 
					                    node.depth_attachment()
 | 
				
			||||||
 | 
					                        .as_ref()
 | 
				
			||||||
 | 
					                        .map(|_| AttachmentReference {
 | 
				
			||||||
 | 
					                            attachment: (color_attachments.len() + input_attachments.len() + offset)
 | 
				
			||||||
 | 
					                                as u32,
 | 
				
			||||||
 | 
					                            layout: ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for attachment in node
 | 
				
			||||||
 | 
					                    .color_attachments()
 | 
				
			||||||
 | 
					                    .iter()
 | 
				
			||||||
 | 
					                    .chain(node.depth_attachment().into_iter())
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    let texture = textures.get(attachment.resource)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    attachments.push(texture.image_view());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    attachment_descriptions.push(AttachmentDescription {
 | 
				
			||||||
 | 
					                        flags: vk::AttachmentDescriptionFlags::default(),
 | 
				
			||||||
 | 
					                        format: texture.format(),
 | 
				
			||||||
 | 
					                        samples: texture.samples(),
 | 
				
			||||||
 | 
					                        load_op: attachment.load_op,
 | 
				
			||||||
 | 
					                        store_op: attachment.store_op,
 | 
				
			||||||
 | 
					                        stencil_load_op: LoadOp::DONT_CARE,
 | 
				
			||||||
 | 
					                        stencil_store_op: StoreOp::DONT_CARE,
 | 
				
			||||||
 | 
					                        initial_layout: attachment.initial_layout,
 | 
				
			||||||
 | 
					                        final_layout: attachment.final_layout,
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                Ok((color_attachments, input_attachments, depth_attachment))
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .collect::<Result<Vec<_>>>()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let subpasses = attachment_refs
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .map(
 | 
				
			||||||
 | 
					                |(color_attachments, input_attachments, depth_attachment)| SubpassInfo {
 | 
				
			||||||
 | 
					                    color_attachments,
 | 
				
			||||||
 | 
					                    resolve_attachments: &[],
 | 
				
			||||||
 | 
					                    input_attachments: &input_attachments,
 | 
				
			||||||
 | 
					                    depth_attachment: *depth_attachment,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let renderpass_info = RenderPassInfo {
 | 
				
			||||||
 | 
					            attachments: &attachment_descriptions,
 | 
				
			||||||
 | 
					            subpasses: &subpasses,
 | 
				
			||||||
 | 
					            dependencies: &dependencies,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let renderpass = RenderPass::new(context.device().clone(), &renderpass_info)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let framebuffer =
 | 
				
			||||||
 | 
					            Framebuffer::new(context.device().clone(), &renderpass, &attachments, extent)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(PassKind::Graphics {
 | 
				
			||||||
 | 
					            renderpass,
 | 
				
			||||||
 | 
					            framebuffer,
 | 
				
			||||||
 | 
					            clear_values,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn transfer<T>(
 | 
				
			||||||
 | 
					        _context: &SharedVulkanContext,
 | 
				
			||||||
 | 
					        _nodes: &Vec<Box<dyn Node>>,
 | 
				
			||||||
 | 
					        textures: &T,
 | 
				
			||||||
 | 
					        dependencies: &HashMap<NodeIndex, Vec<Edge>>,
 | 
				
			||||||
 | 
					        pass_nodes: &[NodeIndex],
 | 
				
			||||||
 | 
					        _extent: Extent,
 | 
				
			||||||
 | 
					    ) -> Result<Self>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        T: Deref<Target = ResourceCache<Texture>>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Get the dependencies of node.
 | 
				
			||||||
 | 
					        let mut src_stage = vk::PipelineStageFlags::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let image_barriers = dependencies
 | 
				
			||||||
 | 
					            .get(&pass_nodes[0])
 | 
				
			||||||
 | 
					            .into_iter()
 | 
				
			||||||
 | 
					            .flat_map(|val| val.iter())
 | 
				
			||||||
 | 
					            .filter_map(|val| {
 | 
				
			||||||
 | 
					                if let ResourceKind::Texture(tex) = val.resource {
 | 
				
			||||||
 | 
					                    Some((val, tex))
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    None
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .map(|(edge, texture)| -> Result<_> {
 | 
				
			||||||
 | 
					                let src = textures.get(texture)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let aspect_mask =
 | 
				
			||||||
 | 
					                    if edge.read_access == vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE {
 | 
				
			||||||
 | 
					                        vk::ImageAspectFlags::DEPTH
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        vk::ImageAspectFlags::COLOR
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                src_stage = edge.write_stage.max(src_stage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Ok(ImageMemoryBarrier {
 | 
				
			||||||
 | 
					                    src_access_mask: edge.write_access,
 | 
				
			||||||
 | 
					                    dst_access_mask: vk::AccessFlags::TRANSFER_READ,
 | 
				
			||||||
 | 
					                    old_layout: edge.layout,
 | 
				
			||||||
 | 
					                    new_layout: ImageLayout::TRANSFER_SRC_OPTIMAL,
 | 
				
			||||||
 | 
					                    src_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
 | 
				
			||||||
 | 
					                    dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
 | 
				
			||||||
 | 
					                    image: src.image(),
 | 
				
			||||||
 | 
					                    subresource_range: vk::ImageSubresourceRange {
 | 
				
			||||||
 | 
					                        aspect_mask,
 | 
				
			||||||
 | 
					                        base_mip_level: 0,
 | 
				
			||||||
 | 
					                        level_count: src.mip_levels(),
 | 
				
			||||||
 | 
					                        base_array_layer: 0,
 | 
				
			||||||
 | 
					                        layer_count: 1,
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    ..Default::default()
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .collect::<Result<Vec<_>>>()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(Self::Transfer {
 | 
				
			||||||
 | 
					            src_stage,
 | 
				
			||||||
 | 
					            image_barriers,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										622
									
								
								modules/khors-graphics/src/rendergraph/rendergraph.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										622
									
								
								modules/khors-graphics/src/rendergraph/rendergraph.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,622 @@
 | 
				
			||||||
 | 
					use super::error::Error;
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					use vulkano::{
 | 
				
			||||||
 | 
					    command_buffer::pool::{CommandPool, CommandPoolCreateFlags, CommandPoolCreateInfo},
 | 
				
			||||||
 | 
					    sync::{fence::Fence, semaphore::Semaphore},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::{node::Node, pass::Pass};
 | 
				
			||||||
 | 
					pub type PassIndex = usize;
 | 
				
			||||||
 | 
					pub type NodeIndex = usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Direct acyclic graph abstraction for renderpasses, barriers and subpass dependencies.
 | 
				
			||||||
 | 
					pub struct RenderGraph {
 | 
				
			||||||
 | 
					    context: SharedVulkanContext,
 | 
				
			||||||
 | 
					    /// The unordered nodes in the arena.
 | 
				
			||||||
 | 
					    nodes: Vec<Box<dyn Node>>,
 | 
				
			||||||
 | 
					    edges: HashMap<NodeIndex, Vec<Edge>>,
 | 
				
			||||||
 | 
					    passes: Vec<Pass>,
 | 
				
			||||||
 | 
					    // Maps a node to a pass index
 | 
				
			||||||
 | 
					    node_pass_map: HashMap<NodeIndex, (PassIndex, u32)>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Data for each frame in flight
 | 
				
			||||||
 | 
					    frames: Vec<FrameData>,
 | 
				
			||||||
 | 
					    extent: Extent,
 | 
				
			||||||
 | 
					    frames_in_flight: usize,
 | 
				
			||||||
 | 
					    current_frame: usize,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl RenderGraph {
 | 
				
			||||||
 | 
					    /// Creates a new empty rendergraph.
 | 
				
			||||||
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        context: SharedVulkanContext,
 | 
				
			||||||
 | 
					        frames_in_flight: usize,
 | 
				
			||||||
 | 
					    ) -> super::error::Result<Self> {
 | 
				
			||||||
 | 
					        let frames = (0..frames_in_flight)
 | 
				
			||||||
 | 
					            .map(|_| FrameData::new(context.clone()).map_err(|e| e.into()))
 | 
				
			||||||
 | 
					            .collect::<super::error::Result<Vec<_>>>()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(Self {
 | 
				
			||||||
 | 
					            context,
 | 
				
			||||||
 | 
					            nodes: Default::default(),
 | 
				
			||||||
 | 
					            edges: Default::default(),
 | 
				
			||||||
 | 
					            passes: Vec::new(),
 | 
				
			||||||
 | 
					            node_pass_map: Default::default(),
 | 
				
			||||||
 | 
					            frames,
 | 
				
			||||||
 | 
					            extent: Extent::new(0, 0),
 | 
				
			||||||
 | 
					            frames_in_flight,
 | 
				
			||||||
 | 
					            current_frame: 0,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Adds a new node into the rendergraph.
 | 
				
			||||||
 | 
					    /// **Note**: The new node won't take effect until [`RenderGraph::build`] is called.
 | 
				
			||||||
 | 
					    pub fn add_node<T: 'static + Node>(&mut self, node: T) -> NodeIndex {
 | 
				
			||||||
 | 
					        let i = self.nodes.len();
 | 
				
			||||||
 | 
					        self.nodes.push(Box::new(node));
 | 
				
			||||||
 | 
					        i
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Add several nodes into the rendergraph.
 | 
				
			||||||
 | 
					    /// Due to the concrete type of iterators, the nodes need to already be boxed.
 | 
				
			||||||
 | 
					    /// Returns the node indices in order.
 | 
				
			||||||
 | 
					    pub fn add_nodes<I: IntoIterator<Item = Box<dyn Node>>>(&mut self, nodes: I) -> Vec<NodeIndex> {
 | 
				
			||||||
 | 
					        nodes
 | 
				
			||||||
 | 
					            .into_iter()
 | 
				
			||||||
 | 
					            .map(|node| {
 | 
				
			||||||
 | 
					                let i = self.nodes.len();
 | 
				
			||||||
 | 
					                self.nodes.push(node);
 | 
				
			||||||
 | 
					                i
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .collect_vec()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn build_edges(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					    ) -> crate::Result<(HashMap<usize, Vec<Edge>>, HashMap<usize, Vec<Edge>>)> {
 | 
				
			||||||
 | 
					        // Iterate all node's read attachments, and find any node which has the same write
 | 
				
			||||||
 | 
					        // attachment before the current node.
 | 
				
			||||||
 | 
					        // Finally, automatically construct edges.
 | 
				
			||||||
 | 
					        let nodes = &self.nodes;
 | 
				
			||||||
 | 
					        let mut edges = HashMap::new();
 | 
				
			||||||
 | 
					        let mut dependencies = HashMap::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        nodes
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .enumerate()
 | 
				
			||||||
 | 
					            .flat_map(|node| {
 | 
				
			||||||
 | 
					                EdgeConstructor {
 | 
				
			||||||
 | 
					                    nodes,
 | 
				
			||||||
 | 
					                    dst: node.0,
 | 
				
			||||||
 | 
					                    reads: node.1.input_attachments().into_iter().cloned(),
 | 
				
			||||||
 | 
					                    kind: EdgeKind::Input,
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                .chain(BufferEdgeConstructor {
 | 
				
			||||||
 | 
					                    nodes,
 | 
				
			||||||
 | 
					                    dst: node.0,
 | 
				
			||||||
 | 
					                    reads: node.1.buffer_reads().into_iter().cloned(),
 | 
				
			||||||
 | 
					                    kind: EdgeKind::Buffer,
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .chain(EdgeConstructor {
 | 
				
			||||||
 | 
					                    nodes,
 | 
				
			||||||
 | 
					                    dst: node.0,
 | 
				
			||||||
 | 
					                    reads: node.1.read_attachments().into_iter().cloned(),
 | 
				
			||||||
 | 
					                    kind: EdgeKind::Sampled,
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .chain(
 | 
				
			||||||
 | 
					                    EdgeConstructor {
 | 
				
			||||||
 | 
					                        nodes,
 | 
				
			||||||
 | 
					                        dst: node.0,
 | 
				
			||||||
 | 
					                        reads: node.1.color_attachments().iter().map(|v| v.resource),
 | 
				
			||||||
 | 
					                        kind: EdgeKind::Attachment,
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    .into_iter()
 | 
				
			||||||
 | 
					                    .filter(|val| !matches!(*val, Err(Error::MissingWrite(_, _, _)))),
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .chain(
 | 
				
			||||||
 | 
					                    EdgeConstructor {
 | 
				
			||||||
 | 
					                        nodes,
 | 
				
			||||||
 | 
					                        dst: node.0,
 | 
				
			||||||
 | 
					                        reads: node.1.output_attachments().into_iter().cloned(),
 | 
				
			||||||
 | 
					                        kind: EdgeKind::Sampled,
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    .into_iter()
 | 
				
			||||||
 | 
					                    .filter(|val| !matches!(*val, Err(Error::MissingWrite(_, _, _)))),
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .try_for_each(
 | 
				
			||||||
 | 
					                |edge: super::error::Result<Edge>| -> super::error::Result<_> {
 | 
				
			||||||
 | 
					                    let edge = edge?;
 | 
				
			||||||
 | 
					                    edges.entry(edge.src).or_insert_with(Vec::new).push(edge);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    dependencies
 | 
				
			||||||
 | 
					                        .entry(edge.dst)
 | 
				
			||||||
 | 
					                        .or_insert_with(Vec::new)
 | 
				
			||||||
 | 
					                        .push(edge);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    Ok(())
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok((edges, dependencies))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn node(&self, node: NodeIndex) -> super::error::Result<&dyn Node> {
 | 
				
			||||||
 | 
					        self.nodes
 | 
				
			||||||
 | 
					            .get(node)
 | 
				
			||||||
 | 
					            .ok_or_else(|| Error::InvalidNodeIndex(node))
 | 
				
			||||||
 | 
					            .map(|val| val.as_ref())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn node_renderpass<'a>(
 | 
				
			||||||
 | 
					        &'a self,
 | 
				
			||||||
 | 
					        node: NodeIndex,
 | 
				
			||||||
 | 
					    ) -> super::error::Result<(&'a RenderPass, u32)> {
 | 
				
			||||||
 | 
					        let (pass, index) = self
 | 
				
			||||||
 | 
					            .node_pass_map
 | 
				
			||||||
 | 
					            .get(&node)
 | 
				
			||||||
 | 
					            .and_then(|(pass, subpass_index)| Some((self.passes.get(*pass)?, *subpass_index)))
 | 
				
			||||||
 | 
					            .ok_or(Error::InvalidNodeIndex(node))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        match pass.kind() {
 | 
				
			||||||
 | 
					            PassKind::Graphics {
 | 
				
			||||||
 | 
					                renderpass,
 | 
				
			||||||
 | 
					                framebuffer: _,
 | 
				
			||||||
 | 
					                clear_values: _,
 | 
				
			||||||
 | 
					            } => Ok((renderpass, index)),
 | 
				
			||||||
 | 
					            PassKind::Transfer { .. } => Err(Error::InvalidNodeKind(
 | 
				
			||||||
 | 
					                node,
 | 
				
			||||||
 | 
					                NodeKind::Graphics,
 | 
				
			||||||
 | 
					                self.nodes[node].node_kind(),
 | 
				
			||||||
 | 
					            )),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Builds or rebuilds the rendergraph and creates appropriate renderpasses and framebuffers.
 | 
				
			||||||
 | 
					    pub fn build<T>(&mut self, textures: T, extent: Extent) -> super::error::Result<()>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        T: Deref<Target = ResourceCache<Texture>>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let (_edges, dependencies) = self.build_edges()?;
 | 
				
			||||||
 | 
					        let (ordered, depths) = topological_sort(&self.nodes, &self.edges)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let context = &self.context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let nodes = &self.nodes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.node_pass_map.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let node_pass_map = &mut self.node_pass_map;
 | 
				
			||||||
 | 
					        node_pass_map.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let passes = &mut self.passes;
 | 
				
			||||||
 | 
					        passes.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Build all graphics nodes
 | 
				
			||||||
 | 
					        let groups = ordered.iter().cloned().group_by(|node| {
 | 
				
			||||||
 | 
					            return (depths[node], nodes[*node].node_kind());
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (key, group) in &groups {
 | 
				
			||||||
 | 
					            println!("New pass: {:?}", key);
 | 
				
			||||||
 | 
					            let pass_nodes = group.collect_vec();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let pass = Pass::new(
 | 
				
			||||||
 | 
					                context,
 | 
				
			||||||
 | 
					                nodes,
 | 
				
			||||||
 | 
					                &textures,
 | 
				
			||||||
 | 
					                &dependencies,
 | 
				
			||||||
 | 
					                pass_nodes,
 | 
				
			||||||
 | 
					                key.1,
 | 
				
			||||||
 | 
					                extent,
 | 
				
			||||||
 | 
					            )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Insert pass into slotmap
 | 
				
			||||||
 | 
					            let pass_index = passes.len();
 | 
				
			||||||
 | 
					            passes.push(pass);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let pass_nodes = passes[pass_index].nodes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Map the node into the pass
 | 
				
			||||||
 | 
					            for (i, node) in pass_nodes.iter().enumerate() {
 | 
				
			||||||
 | 
					                node_pass_map.insert(*node, (pass_index, i as u32));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.extent = extent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Begins the current frame and ensures resources are ready by waiting on fences.
 | 
				
			||||||
 | 
					    // Begins recording of the commandbuffers.
 | 
				
			||||||
 | 
					    // Returns the current frame in flight
 | 
				
			||||||
 | 
					    pub fn begin(&self) -> super::error::Result<usize> {
 | 
				
			||||||
 | 
					        let frame = &self.frames[self.current_frame];
 | 
				
			||||||
 | 
					        let device = self.context.device();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Make sure frame is available before beginning execution
 | 
				
			||||||
 | 
					        fence::wait(device, &[frame.fence], true)?;
 | 
				
			||||||
 | 
					        fence::reset(device, &[frame.fence])?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Reset commandbuffers for this frame
 | 
				
			||||||
 | 
					        frame.commandpool.reset(false)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get the commandbuffer for this frame
 | 
				
			||||||
 | 
					        let commandbuffer = &frame.commandbuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Start recording
 | 
				
			||||||
 | 
					        commandbuffer.begin(CommandBufferUsageFlags::ONE_TIME_SUBMIT)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(self.current_frame)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Executes the whole rendergraph by starting renderpass recording and filling it using the
 | 
				
			||||||
 | 
					    // node execution functions. Submits the resulting commandbuffer.
 | 
				
			||||||
 | 
					    pub fn execute(&mut self, world: &mut World) -> super::error::Result<()> {
 | 
				
			||||||
 | 
					        // Reset all commandbuffers for this frame
 | 
				
			||||||
 | 
					        let frame = &mut self.frames[self.current_frame];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let nodes = &mut self.nodes;
 | 
				
			||||||
 | 
					        let passes = &self.passes;
 | 
				
			||||||
 | 
					        let extent = self.extent;
 | 
				
			||||||
 | 
					        let current_frame = self.current_frame;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let cmd = &frame.commandbuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Execute all nodes
 | 
				
			||||||
 | 
					        passes
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .try_for_each(|pass| -> super::error::Result<()> {
 | 
				
			||||||
 | 
					                pass.execute(world, &cmd, nodes, current_frame, extent)
 | 
				
			||||||
 | 
					            })?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Ends and submits recording of commandbuffer for the current frame, and increments the
 | 
				
			||||||
 | 
					    /// current_frame.
 | 
				
			||||||
 | 
					    pub fn end(&mut self) -> super::error::Result<()> {
 | 
				
			||||||
 | 
					        let frame = &self.frames[self.current_frame];
 | 
				
			||||||
 | 
					        let commandbuffer = &frame.commandbuffer;
 | 
				
			||||||
 | 
					        commandbuffer.end()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Submit the results
 | 
				
			||||||
 | 
					        commandbuffer.submit(
 | 
				
			||||||
 | 
					            self.context.graphics_queue(),
 | 
				
			||||||
 | 
					            &[frame.wait_semaphore],
 | 
				
			||||||
 | 
					            &[frame.signal_semaphore],
 | 
				
			||||||
 | 
					            frame.fence,
 | 
				
			||||||
 | 
					            &[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT],
 | 
				
			||||||
 | 
					        )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Move to the next frame in flight and wrap around to n-buffer
 | 
				
			||||||
 | 
					        self.current_frame = (self.current_frame + 1) % self.frames_in_flight;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get a reference to the current signal semaphore for the specified frame
 | 
				
			||||||
 | 
					    pub fn signal_semaphore(&self, current_frame: usize) -> Semaphore {
 | 
				
			||||||
 | 
					        self.frames[current_frame].signal_semaphore
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get a reference to the current wait semaphore for the specified frame
 | 
				
			||||||
 | 
					    pub fn wait_semaphore(&self, current_frame: usize) -> Semaphore {
 | 
				
			||||||
 | 
					        self.frames[current_frame].wait_semaphore
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get a reference to the current fence for the specified frame
 | 
				
			||||||
 | 
					    pub fn fence(&self, current_frame: usize) -> Fence {
 | 
				
			||||||
 | 
					        self.frames[current_frame].fence
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn commandbuffer(&self, current_frame: usize) -> &CommandBuffer {
 | 
				
			||||||
 | 
					        &self.frames[current_frame].commandbuffer
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 | 
				
			||||||
 | 
					enum VisitedState {
 | 
				
			||||||
 | 
					    Pending,
 | 
				
			||||||
 | 
					    Visited,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Depth = u32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Toplogically sorts the graph provided by nodes and edges.
 | 
				
			||||||
 | 
					/// Returns a tuple containing a dense array of ordered node indices, and a map containing each node's maximum depth.
 | 
				
			||||||
 | 
					// TODO: Move graph functionality into separate crate.
 | 
				
			||||||
 | 
					fn topological_sort(
 | 
				
			||||||
 | 
					    nodes: &Vec<Box<dyn Node>>,
 | 
				
			||||||
 | 
					    edges: &HashMap<NodeIndex, Vec<Edge>>,
 | 
				
			||||||
 | 
					) -> super::error::Result<(Vec<NodeIndex>, HashMap<NodeIndex, Depth>)> {
 | 
				
			||||||
 | 
					    fn internal(
 | 
				
			||||||
 | 
					        stack: &mut Vec<NodeIndex>,
 | 
				
			||||||
 | 
					        visited: &mut HashMap<NodeIndex, VisitedState>,
 | 
				
			||||||
 | 
					        depths: &mut HashMap<NodeIndex, Depth>,
 | 
				
			||||||
 | 
					        current_node: NodeIndex,
 | 
				
			||||||
 | 
					        edges: &HashMap<NodeIndex, Vec<Edge>>,
 | 
				
			||||||
 | 
					        depth: Depth,
 | 
				
			||||||
 | 
					    ) -> super::error::Result<()> {
 | 
				
			||||||
 | 
					        // Update maximum recursion depth
 | 
				
			||||||
 | 
					        depths
 | 
				
			||||||
 | 
					            .entry(current_node)
 | 
				
			||||||
 | 
					            .and_modify(|d| *d = (*d).max(depth))
 | 
				
			||||||
 | 
					            .or_insert(depth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Node is already visited
 | 
				
			||||||
 | 
					        match visited.get(¤t_node) {
 | 
				
			||||||
 | 
					            Some(VisitedState::Pending) => return Err(Error::DependencyCycle),
 | 
				
			||||||
 | 
					            Some(VisitedState::Visited) => return Ok(()),
 | 
				
			||||||
 | 
					            _ => {}
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        visited.insert(current_node, VisitedState::Pending);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Add all children of `node`, before node to the stack.
 | 
				
			||||||
 | 
					        edges
 | 
				
			||||||
 | 
					            .get(¤t_node)
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .flat_map(|node_edges| node_edges.iter())
 | 
				
			||||||
 | 
					            .try_for_each(|edge| {
 | 
				
			||||||
 | 
					                internal(
 | 
				
			||||||
 | 
					                    stack,
 | 
				
			||||||
 | 
					                    visited,
 | 
				
			||||||
 | 
					                    depths,
 | 
				
			||||||
 | 
					                    edge.dst,
 | 
				
			||||||
 | 
					                    edges,
 | 
				
			||||||
 | 
					                    // Break depth if sampling is required since they can't share subpasses
 | 
				
			||||||
 | 
					                    match edge.kind {
 | 
				
			||||||
 | 
					                        EdgeKind::Sampled | EdgeKind::Buffer => depth + 1,
 | 
				
			||||||
 | 
					                        EdgeKind::Attachment | EdgeKind::Input => depth,
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            })?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        stack.push(current_node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        visited.insert(current_node, VisitedState::Visited);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let nodes_iter = nodes.into_iter();
 | 
				
			||||||
 | 
					    let cap = nodes_iter.size_hint().1.unwrap_or_default();
 | 
				
			||||||
 | 
					    let mut stack = Vec::with_capacity(cap);
 | 
				
			||||||
 | 
					    let mut visited = HashMap::with_capacity(cap);
 | 
				
			||||||
 | 
					    let mut depths = HashMap::with_capacity(cap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (node, _) in nodes_iter.enumerate() {
 | 
				
			||||||
 | 
					        internal(&mut stack, &mut visited, &mut depths, node, edges, 0)?;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // stack.reverse();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok((stack, depths))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 | 
				
			||||||
 | 
					pub struct Edge {
 | 
				
			||||||
 | 
					    pub src: NodeIndex,
 | 
				
			||||||
 | 
					    pub dst: NodeIndex,
 | 
				
			||||||
 | 
					    pub resource: ResourceKind,
 | 
				
			||||||
 | 
					    pub write_stage: PipelineStageFlags,
 | 
				
			||||||
 | 
					    pub read_stage: PipelineStageFlags,
 | 
				
			||||||
 | 
					    pub write_access: vk::AccessFlags,
 | 
				
			||||||
 | 
					    pub read_access: vk::AccessFlags,
 | 
				
			||||||
 | 
					    pub layout: ImageLayout,
 | 
				
			||||||
 | 
					    pub kind: EdgeKind,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl std::ops::Deref for Edge {
 | 
				
			||||||
 | 
					    type Target = ResourceKind;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn deref(&self) -> &Self::Target {
 | 
				
			||||||
 | 
					        &self.resource
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 | 
				
			||||||
 | 
					pub enum ResourceKind {
 | 
				
			||||||
 | 
					    Texture(Handle<Texture>),
 | 
				
			||||||
 | 
					    Buffer(vk::Buffer),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<vk::Buffer> for ResourceKind {
 | 
				
			||||||
 | 
					    fn from(val: vk::Buffer) -> Self {
 | 
				
			||||||
 | 
					        Self::Buffer(val)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<Handle<Texture>> for ResourceKind {
 | 
				
			||||||
 | 
					    fn from(val: Handle<Texture>) -> Self {
 | 
				
			||||||
 | 
					        Self::Texture(val)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 | 
				
			||||||
 | 
					pub enum EdgeKind {
 | 
				
			||||||
 | 
					    /// Dependency is sampled and requires the whole attachment to be ready.
 | 
				
			||||||
 | 
					    Sampled,
 | 
				
			||||||
 | 
					    /// Dependency is used as input attachment and can use dependency by region.
 | 
				
			||||||
 | 
					    Input,
 | 
				
			||||||
 | 
					    /// The attachment is loaded and written to. Depend on earlier nodes
 | 
				
			||||||
 | 
					    Attachment,
 | 
				
			||||||
 | 
					    Buffer,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for EdgeKind {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self::Sampled
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct FrameData {
 | 
				
			||||||
 | 
					    context: SharedVulkanContext,
 | 
				
			||||||
 | 
					    fence: Fence,
 | 
				
			||||||
 | 
					    commandpool: CommandPool,
 | 
				
			||||||
 | 
					    commandbuffer: CommandBuffer,
 | 
				
			||||||
 | 
					    wait_semaphore: Semaphore,
 | 
				
			||||||
 | 
					    signal_semaphore: Semaphore,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl FrameData {
 | 
				
			||||||
 | 
					    pub fn new(context: SharedVulkanContext) -> super::error::Result<Self> {
 | 
				
			||||||
 | 
					        let commandpool = CommandPool::new(
 | 
				
			||||||
 | 
					            context.device().clone(),
 | 
				
			||||||
 | 
					            CommandPoolCreateInfo {
 | 
				
			||||||
 | 
					                flags: CommandPoolCreateFlags {
 | 
				
			||||||
 | 
					                    ..Default::default()
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                queue_family_index: context.queue_families().graphics().unwrap(),
 | 
				
			||||||
 | 
					                ..Default::default()
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let commandbuffer = commandpool.allocate_one()?;
 | 
				
			||||||
 | 
					        let fence = fence::create(context.device(), true)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let wait_semaphore = semaphore::create(context.device())?;
 | 
				
			||||||
 | 
					        let signal_semaphore = semaphore::create(context.device())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(Self {
 | 
				
			||||||
 | 
					            context,
 | 
				
			||||||
 | 
					            fence,
 | 
				
			||||||
 | 
					            commandpool,
 | 
				
			||||||
 | 
					            commandbuffer,
 | 
				
			||||||
 | 
					            wait_semaphore,
 | 
				
			||||||
 | 
					            signal_semaphore,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct EdgeConstructor<'a, I> {
 | 
				
			||||||
 | 
					    nodes: &'a Vec<Box<dyn Node>>,
 | 
				
			||||||
 | 
					    dst: NodeIndex,
 | 
				
			||||||
 | 
					    reads: I,
 | 
				
			||||||
 | 
					    kind: EdgeKind,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a, I: Iterator<Item = Handle<Texture>>> Iterator for EdgeConstructor<'a, I> {
 | 
				
			||||||
 | 
					    type Item = super::error::Result<Edge>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn next(&mut self) -> Option<Self::Item> {
 | 
				
			||||||
 | 
					        self.reads
 | 
				
			||||||
 | 
					            .next()
 | 
				
			||||||
 | 
					            // Find the corresponding write attachment
 | 
				
			||||||
 | 
					            .map(move |read| {
 | 
				
			||||||
 | 
					                self.nodes
 | 
				
			||||||
 | 
					                    .iter()
 | 
				
			||||||
 | 
					                    .enumerate()
 | 
				
			||||||
 | 
					                    .take_while(|(src, _)| *src != self.dst)
 | 
				
			||||||
 | 
					                    .filter_map(|(src, src_node)| {
 | 
				
			||||||
 | 
					                        // Found color attachment output
 | 
				
			||||||
 | 
					                        if let Some(write) = src_node
 | 
				
			||||||
 | 
					                            .color_attachments()
 | 
				
			||||||
 | 
					                            .iter()
 | 
				
			||||||
 | 
					                            .find(|w| w.resource == read)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            Some(Edge {
 | 
				
			||||||
 | 
					                                src,
 | 
				
			||||||
 | 
					                                dst: self.dst,
 | 
				
			||||||
 | 
					                                resource: read.into(),
 | 
				
			||||||
 | 
					                                layout: write.final_layout,
 | 
				
			||||||
 | 
					                                write_stage: vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
 | 
				
			||||||
 | 
					                                read_stage: vk::PipelineStageFlags::FRAGMENT_SHADER,
 | 
				
			||||||
 | 
					                                write_access: vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
 | 
				
			||||||
 | 
					                                read_access: vk::AccessFlags::SHADER_READ,
 | 
				
			||||||
 | 
					                                kind: self.kind,
 | 
				
			||||||
 | 
					                            })
 | 
				
			||||||
 | 
					                            // Found transfer attachment output
 | 
				
			||||||
 | 
					                        } else if let Some(_) =
 | 
				
			||||||
 | 
					                            src_node.output_attachments().iter().find(|d| **d == read)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            Some(Edge {
 | 
				
			||||||
 | 
					                                src,
 | 
				
			||||||
 | 
					                                dst: self.dst,
 | 
				
			||||||
 | 
					                                resource: read.into(),
 | 
				
			||||||
 | 
					                                layout: ImageLayout::TRANSFER_DST_OPTIMAL,
 | 
				
			||||||
 | 
					                                write_stage: vk::PipelineStageFlags::TRANSFER,
 | 
				
			||||||
 | 
					                                read_stage: vk::PipelineStageFlags::FRAGMENT_SHADER,
 | 
				
			||||||
 | 
					                                write_access: vk::AccessFlags::TRANSFER_WRITE,
 | 
				
			||||||
 | 
					                                read_access: vk::AccessFlags::SHADER_READ,
 | 
				
			||||||
 | 
					                                kind: self.kind,
 | 
				
			||||||
 | 
					                            })
 | 
				
			||||||
 | 
					                        } else if let Some(write) = src_node
 | 
				
			||||||
 | 
					                            .depth_attachment()
 | 
				
			||||||
 | 
					                            .as_ref()
 | 
				
			||||||
 | 
					                            .filter(|d| d.resource == read)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            Some(Edge {
 | 
				
			||||||
 | 
					                                src,
 | 
				
			||||||
 | 
					                                dst: self.dst,
 | 
				
			||||||
 | 
					                                resource: read.into(),
 | 
				
			||||||
 | 
					                                layout: write.final_layout,
 | 
				
			||||||
 | 
					                                // Write stage is between
 | 
				
			||||||
 | 
					                                write_stage: vk::PipelineStageFlags::LATE_FRAGMENT_TESTS,
 | 
				
			||||||
 | 
					                                read_stage: vk::PipelineStageFlags::FRAGMENT_SHADER,
 | 
				
			||||||
 | 
					                                write_access: vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
 | 
				
			||||||
 | 
					                                read_access: vk::AccessFlags::SHADER_READ,
 | 
				
			||||||
 | 
					                                kind: self.kind,
 | 
				
			||||||
 | 
					                            })
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            None
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    .last()
 | 
				
			||||||
 | 
					                    .ok_or(Error::MissingWrite(
 | 
				
			||||||
 | 
					                        self.dst,
 | 
				
			||||||
 | 
					                        self.nodes[self.dst].debug_name(),
 | 
				
			||||||
 | 
					                        read.into(),
 | 
				
			||||||
 | 
					                    ))
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct BufferEdgeConstructor<'a, I> {
 | 
				
			||||||
 | 
					    nodes: &'a Vec<Box<dyn Node>>,
 | 
				
			||||||
 | 
					    dst: NodeIndex,
 | 
				
			||||||
 | 
					    reads: I,
 | 
				
			||||||
 | 
					    kind: EdgeKind,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a, I: Iterator<Item = vk::Buffer>> Iterator for BufferEdgeConstructor<'a, I> {
 | 
				
			||||||
 | 
					    type Item = crate::Result<Edge>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn next(&mut self) -> Option<Self::Item> {
 | 
				
			||||||
 | 
					        self.reads
 | 
				
			||||||
 | 
					            .next()
 | 
				
			||||||
 | 
					            // Find the corresponding write attachment
 | 
				
			||||||
 | 
					            .map(move |read| {
 | 
				
			||||||
 | 
					                self.nodes
 | 
				
			||||||
 | 
					                    .iter()
 | 
				
			||||||
 | 
					                    .enumerate()
 | 
				
			||||||
 | 
					                    .take_while(|(src, _)| *src != self.dst)
 | 
				
			||||||
 | 
					                    .filter_map(|(src, src_node)| {
 | 
				
			||||||
 | 
					                        // Found color attachment output
 | 
				
			||||||
 | 
					                        if let Some(_) = src_node.buffer_writes().iter().find(|w| **w == read) {
 | 
				
			||||||
 | 
					                            Some(Edge {
 | 
				
			||||||
 | 
					                                src,
 | 
				
			||||||
 | 
					                                dst: self.dst,
 | 
				
			||||||
 | 
					                                resource: read.into(),
 | 
				
			||||||
 | 
					                                layout: Default::default(),
 | 
				
			||||||
 | 
					                                write_stage: vk::PipelineStageFlags::TRANSFER,
 | 
				
			||||||
 | 
					                                read_stage: vk::PipelineStageFlags::VERTEX_SHADER,
 | 
				
			||||||
 | 
					                                write_access: vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
 | 
				
			||||||
 | 
					                                read_access: vk::AccessFlags::SHADER_READ,
 | 
				
			||||||
 | 
					                                kind: self.kind,
 | 
				
			||||||
 | 
					                            })
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            None
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    .last()
 | 
				
			||||||
 | 
					                    .ok_or(Error::MissingWrite(
 | 
				
			||||||
 | 
					                        self.dst,
 | 
				
			||||||
 | 
					                        self.nodes[self.dst].debug_name(),
 | 
				
			||||||
 | 
					                        read.into(),
 | 
				
			||||||
 | 
					                    ))
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1
									
								
								modules/khors-graphics/src/temp/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								modules/khors-graphics/src/temp/mod.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					pub mod model;
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
use super::vulkan::vertex::{Position, Normal};
 | 
					use crate::vulkan::vertex::{Position, Normal};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const POSITIONS: [Position; 531] = [
 | 
					pub const POSITIONS: [Position; 531] = [
 | 
				
			||||||
    Position {
 | 
					    Position {
 | 
				
			||||||
							
								
								
									
										14
									
								
								modules/khors-graphics/src/vulkan/framebuffer.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								modules/khors-graphics/src/vulkan/framebuffer.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use vulkano::{image::view::ImageView, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass}};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn make_framebuffer(render_pass: Arc<RenderPass>, attachments: Vec<Arc<ImageView>>) -> Result<Arc<Framebuffer>, vulkano::Validated<vulkano::VulkanError>> {
 | 
				
			||||||
 | 
					    let extent = attachments[0].image().extent();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    Framebuffer::new(render_pass.clone(), FramebufferCreateInfo {
 | 
				
			||||||
 | 
					        attachments,
 | 
				
			||||||
 | 
					        extent: [extent[0], extent[1]],
 | 
				
			||||||
 | 
					        layers: 1,
 | 
				
			||||||
 | 
					        ..Default::default()
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,2 +1,4 @@
 | 
				
			||||||
// pub mod pipeline;
 | 
					pub mod pipeline;
 | 
				
			||||||
 | 
					pub mod framebuffer;
 | 
				
			||||||
pub mod vertex;
 | 
					pub mod vertex;
 | 
				
			||||||
 | 
					pub mod renderpass;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,14 +17,14 @@ use vulkano::{
 | 
				
			||||||
        GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo,
 | 
					        GraphicsPipeline, PipelineLayout, PipelineShaderStageCreateInfo,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    render_pass::{RenderPass, Subpass},
 | 
					    render_pass::{RenderPass, Subpass},
 | 
				
			||||||
    shader::ShaderModule,
 | 
					    shader::EntryPoint,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::vertex::{Normal, Position};
 | 
					use super::vertex::{Normal, Position};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct PipelineInfo {
 | 
					pub struct PipelineInfo {
 | 
				
			||||||
    pub vs: Arc<ShaderModule>,
 | 
					    pub vs: EntryPoint,
 | 
				
			||||||
    pub fs: Arc<ShaderModule>,
 | 
					    pub fs: EntryPoint,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn make_pipeline(
 | 
					pub fn make_pipeline(
 | 
				
			||||||
| 
						 | 
					@ -32,15 +32,12 @@ pub fn make_pipeline(
 | 
				
			||||||
    pipeline_info: &PipelineInfo,
 | 
					    pipeline_info: &PipelineInfo,
 | 
				
			||||||
    pass_info: &PassInfo,
 | 
					    pass_info: &PassInfo,
 | 
				
			||||||
) -> Arc<GraphicsPipeline> {
 | 
					) -> Arc<GraphicsPipeline> {
 | 
				
			||||||
    let vs = pipeline_info.vs.entry_point("main").unwrap();
 | 
					 | 
				
			||||||
    let fs = pipeline_info.fs.entry_point("main").unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let vertex_input_state = [Position::per_vertex(), Normal::per_vertex()]
 | 
					    let vertex_input_state = [Position::per_vertex(), Normal::per_vertex()]
 | 
				
			||||||
        .definition(&vs)
 | 
					        .definition(&pipeline_info.vs)
 | 
				
			||||||
        .unwrap();
 | 
					        .unwrap();
 | 
				
			||||||
    let stages = [
 | 
					    let stages = [
 | 
				
			||||||
        PipelineShaderStageCreateInfo::new(vs),
 | 
					        PipelineShaderStageCreateInfo::new(pipeline_info.vs.clone()),
 | 
				
			||||||
        PipelineShaderStageCreateInfo::new(fs),
 | 
					        PipelineShaderStageCreateInfo::new(pipeline_info.fs.clone()),
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    let layout = PipelineLayout::new(
 | 
					    let layout = PipelineLayout::new(
 | 
				
			||||||
        device.clone(),
 | 
					        device.clone(),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										116
									
								
								modules/khors-graphics/src/vulkan/renderpass.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								modules/khors-graphics/src/vulkan/renderpass.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,116 @@
 | 
				
			||||||
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use vulkano::{device::Device, format::Format, render_pass::{AttachmentDescription, AttachmentReference, RenderPass as VkRenderPass, RenderPassCreateFlags, RenderPassCreateInfo, SubpassDependency, SubpassDescription, SubpassDescriptionFlags}};
 | 
				
			||||||
 | 
					use vulkano_util::renderer::VulkanoWindowRenderer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default, Debug)]
 | 
				
			||||||
 | 
					pub struct SubpassInfo {
 | 
				
			||||||
 | 
					    pub color_attachments: Vec<Option<AttachmentReference>>,
 | 
				
			||||||
 | 
					    pub color_resolve_attachments: Vec<Option<AttachmentReference>>,
 | 
				
			||||||
 | 
					    pub input_attachments: Vec<Option<AttachmentReference>>,
 | 
				
			||||||
 | 
					    pub depth_stencil_attachment: Option<AttachmentReference>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<SubpassInfo> for SubpassDescription {
 | 
				
			||||||
 | 
					    fn from(value: SubpassInfo) -> Self {
 | 
				
			||||||
 | 
					        SubpassDescription {
 | 
				
			||||||
 | 
					            flags: SubpassDescriptionFlags::default(),
 | 
				
			||||||
 | 
					            color_attachments: value.color_attachments,
 | 
				
			||||||
 | 
					            color_resolve_attachments: value.color_resolve_attachments,
 | 
				
			||||||
 | 
					            input_attachments: value.input_attachments,
 | 
				
			||||||
 | 
					            depth_stencil_attachment: value.depth_stencil_attachment,
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn make_subpass_description(subpass_info: Arc<SubpassInfo>) -> SubpassDescription {
 | 
				
			||||||
 | 
					    SubpassDescription {
 | 
				
			||||||
 | 
					        flags: SubpassDescriptionFlags::default(),
 | 
				
			||||||
 | 
					        color_attachments: subpass_info.color_attachments.clone(),
 | 
				
			||||||
 | 
					        color_resolve_attachments: subpass_info.color_resolve_attachments.clone(),
 | 
				
			||||||
 | 
					        input_attachments: subpass_info.input_attachments.clone(),
 | 
				
			||||||
 | 
					        depth_stencil_attachment: subpass_info.depth_stencil_attachment.clone(),
 | 
				
			||||||
 | 
					        ..Default::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default)]
 | 
				
			||||||
 | 
					pub struct RenderPassInfo {
 | 
				
			||||||
 | 
					    pub attachments: Vec<AttachmentDescription>,
 | 
				
			||||||
 | 
					    pub subpasses: Vec<Arc<SubpassInfo>>,
 | 
				
			||||||
 | 
					    pub dependencies: Vec<SubpassDependency>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct RenderPass {
 | 
				
			||||||
 | 
					    device: Arc<Device>,
 | 
				
			||||||
 | 
					    render_pass: Arc<VkRenderPass>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl RenderPass {
 | 
				
			||||||
 | 
					    pub fn new(device: Arc<vulkano::device::Device>, info: Arc<RenderPassInfo>) -> Self {
 | 
				
			||||||
 | 
					        let vk_subpasses = info.subpasses.iter().map(|subpass| make_subpass_description(subpass.clone())).collect::<Vec<SubpassDescription>>();
 | 
				
			||||||
 | 
					        let render_pass_create_info = RenderPassCreateInfo {
 | 
				
			||||||
 | 
					            attachments: info.attachments.clone(),
 | 
				
			||||||
 | 
					            dependencies: info.dependencies.clone(),
 | 
				
			||||||
 | 
					            subpasses: vk_subpasses,
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let render_pass = VkRenderPass::new(device.clone(), render_pass_create_info).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            device: device.clone(),
 | 
				
			||||||
 | 
					            render_pass
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn renderpass(&self) -> Arc<VkRenderPass> {
 | 
				
			||||||
 | 
					        self.render_pass.clone()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn make_render_pass(device: Arc<vulkano::device::Device>, renderer: &mut VulkanoWindowRenderer) -> Arc<VkRenderPass> {
 | 
				
			||||||
 | 
					    let color_attachment = AttachmentDescription {
 | 
				
			||||||
 | 
					        format: renderer.swapchain_format(),
 | 
				
			||||||
 | 
					        samples: vulkano::image::SampleCount::Sample1,
 | 
				
			||||||
 | 
					        load_op: vulkano::render_pass::AttachmentLoadOp::Clear,
 | 
				
			||||||
 | 
					        store_op: vulkano::render_pass::AttachmentStoreOp::Store,
 | 
				
			||||||
 | 
					        initial_layout: vulkano::image::ImageLayout::ColorAttachmentOptimal,
 | 
				
			||||||
 | 
					        final_layout: vulkano::image::ImageLayout::ColorAttachmentOptimal,
 | 
				
			||||||
 | 
					        ..Default::default()
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    let depth_stencil_attachment = AttachmentDescription {
 | 
				
			||||||
 | 
					        format: Format::D16_UNORM,
 | 
				
			||||||
 | 
					        samples: vulkano::image::SampleCount::Sample1,
 | 
				
			||||||
 | 
					        load_op: vulkano::render_pass::AttachmentLoadOp::Clear,
 | 
				
			||||||
 | 
					        store_op: vulkano::render_pass::AttachmentStoreOp::DontCare,
 | 
				
			||||||
 | 
					        initial_layout: vulkano::image::ImageLayout::DepthStencilAttachmentOptimal,
 | 
				
			||||||
 | 
					        final_layout: vulkano::image::ImageLayout::DepthStencilAttachmentOptimal,
 | 
				
			||||||
 | 
					        ..Default::default()
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let color_attachment_reference = AttachmentReference {
 | 
				
			||||||
 | 
					        attachment: 0,
 | 
				
			||||||
 | 
					        layout: color_attachment.initial_layout,
 | 
				
			||||||
 | 
					        ..Default::default()
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let depth_stencil_attachment_reference = AttachmentReference {
 | 
				
			||||||
 | 
					        attachment: 1,
 | 
				
			||||||
 | 
					        layout: depth_stencil_attachment.initial_layout,
 | 
				
			||||||
 | 
					        ..Default::default()
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    let subpass_description = SubpassDescription {
 | 
				
			||||||
 | 
					        color_attachments: vec![Some(color_attachment_reference)],
 | 
				
			||||||
 | 
					        depth_stencil_attachment: Some(depth_stencil_attachment_reference),
 | 
				
			||||||
 | 
					        ..Default::default()
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    VkRenderPass::new(device.clone(), RenderPassCreateInfo {
 | 
				
			||||||
 | 
					        attachments: vec![color_attachment, depth_stencil_attachment],
 | 
				
			||||||
 | 
					        subpasses: vec![subpass_description],
 | 
				
			||||||
 | 
					        ..Default::default()
 | 
				
			||||||
 | 
					    }).unwrap()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										13
									
								
								modules/khors-window/src/window.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								modules/khors-window/src/window.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					use winit::event_loop::EventLoopBuilder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Window {
 | 
				
			||||||
 | 
					    handle: winit::window::Window,
 | 
				
			||||||
 | 
					    event_loop: winit::event_loop::EventLoop<()>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Window {
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        let event_loop = EventLoopBuilder::new().build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										29
									
								
								vendor/egui-snarl/Cargo.toml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								vendor/egui-snarl/Cargo.toml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,29 @@
 | 
				
			||||||
 | 
					[package]
 | 
				
			||||||
 | 
					name = "egui-snarl"
 | 
				
			||||||
 | 
					version = "0.2.1"
 | 
				
			||||||
 | 
					edition = "2021"
 | 
				
			||||||
 | 
					description = "Node-graphs for egui"
 | 
				
			||||||
 | 
					license = "MIT OR Apache-2.0"
 | 
				
			||||||
 | 
					documentation = "https://docs.rs/egui-snarl"
 | 
				
			||||||
 | 
					keywords = ["egui", "node", "graph", "ui", "node-graph"]
 | 
				
			||||||
 | 
					categories = ["gui", "visualization"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[features]
 | 
				
			||||||
 | 
					serde = ["dep:serde", "egui/serde", "slab/serde"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies]
 | 
				
			||||||
 | 
					egui = { version = "0.27" }
 | 
				
			||||||
 | 
					slab = { version = "0.4" }
 | 
				
			||||||
 | 
					serde = { version = "1.0", features = ["derive"], optional = true }
 | 
				
			||||||
 | 
					tiny-fn = { version = "0.1.6" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					egui-probe = { version = "0.2", features = ["derive"], optional = true }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dev-dependencies]
 | 
				
			||||||
 | 
					eframe = { version = "0.26", features = ["serde", "persistence"] }
 | 
				
			||||||
 | 
					egui_extras = { version = "0.26", features = ["all_loaders"] }
 | 
				
			||||||
 | 
					syn = { version = "2.0", features = ["extra-traits"] }
 | 
				
			||||||
 | 
					serde_json = { version = "1.0" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
 | 
				
			||||||
 | 
					wasm-bindgen-futures = "0.4"
 | 
				
			||||||
							
								
								
									
										696
									
								
								vendor/egui-snarl/src/lib.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										696
									
								
								vendor/egui-snarl/src/lib.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,696 @@
 | 
				
			||||||
 | 
					//!
 | 
				
			||||||
 | 
					//! # egui-snarl
 | 
				
			||||||
 | 
					//!
 | 
				
			||||||
 | 
					//! Provides a node-graph container for egui.
 | 
				
			||||||
 | 
					//!
 | 
				
			||||||
 | 
					//!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#![deny(missing_docs)]
 | 
				
			||||||
 | 
					#![deny(clippy::correctness, clippy::complexity, clippy::perf, clippy::style)]
 | 
				
			||||||
 | 
					// #![warn(clippy::pedantic)]
 | 
				
			||||||
 | 
					#![allow(clippy::inline_always)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub mod ui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::ops::{Index, IndexMut};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use egui::{ahash::HashSet, Pos2};
 | 
				
			||||||
 | 
					use slab::Slab;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> Default for Snarl<T> {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Snarl::new()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Node identifier.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// This is newtype wrapper around [`usize`] that implements
 | 
				
			||||||
 | 
					/// necessary traits, but omits arithmetic operations.
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 | 
				
			||||||
 | 
					#[repr(transparent)]
 | 
				
			||||||
 | 
					#[cfg_attr(
 | 
				
			||||||
 | 
					    feature = "serde",
 | 
				
			||||||
 | 
					    derive(serde::Serialize, serde::Deserialize),
 | 
				
			||||||
 | 
					    serde(transparent)
 | 
				
			||||||
 | 
					)]
 | 
				
			||||||
 | 
					pub struct NodeId(pub usize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Debug)]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
 | 
					struct Node<T> {
 | 
				
			||||||
 | 
					    /// Node generic value.
 | 
				
			||||||
 | 
					    value: T,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Position of the top-left corner of the node.
 | 
				
			||||||
 | 
					    /// This does not include frame margin.
 | 
				
			||||||
 | 
					    pos: egui::Pos2,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Flag indicating that the node is open - not collapsed.
 | 
				
			||||||
 | 
					    open: bool,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Output pin identifier.
 | 
				
			||||||
 | 
					/// Cosists of node id and pin index.
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
 | 
					pub struct OutPinId {
 | 
				
			||||||
 | 
					    /// Node id.
 | 
				
			||||||
 | 
					    pub node: NodeId,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Output pin index.
 | 
				
			||||||
 | 
					    pub output: usize,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Input pin identifier. Cosists of node id and pin index.
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
 | 
					pub struct InPinId {
 | 
				
			||||||
 | 
					    /// Node id.
 | 
				
			||||||
 | 
					    pub node: NodeId,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Input pin index.
 | 
				
			||||||
 | 
					    pub input: usize,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Connection between two nodes.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Nodes may support multiple connections to the same input or output.
 | 
				
			||||||
 | 
					/// But duplicate connections between same input and the same output are not allowed.
 | 
				
			||||||
 | 
					/// Attempt to insert existing connection will be ignored.
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
 | 
					struct Wire {
 | 
				
			||||||
 | 
					    out_pin: OutPinId,
 | 
				
			||||||
 | 
					    in_pin: InPinId,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Debug)]
 | 
				
			||||||
 | 
					struct Wires {
 | 
				
			||||||
 | 
					    wires: HashSet<Wire>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "serde")]
 | 
				
			||||||
 | 
					impl serde::Serialize for Wires {
 | 
				
			||||||
 | 
					    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        S: serde::Serializer,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        use serde::ser::SerializeSeq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut seq = serializer.serialize_seq(Some(self.wires.len()))?;
 | 
				
			||||||
 | 
					        for wire in &self.wires {
 | 
				
			||||||
 | 
					            seq.serialize_element(&wire)?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        seq.end()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "serde")]
 | 
				
			||||||
 | 
					impl<'de> serde::Deserialize<'de> for Wires {
 | 
				
			||||||
 | 
					    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        D: serde::Deserializer<'de>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        struct Visitor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        impl<'de> serde::de::Visitor<'de> for Visitor {
 | 
				
			||||||
 | 
					            type Value = HashSet<Wire>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
 | 
				
			||||||
 | 
					                formatter.write_str("a sequence of wires")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
 | 
				
			||||||
 | 
					            where
 | 
				
			||||||
 | 
					                A: serde::de::SeqAccess<'de>,
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                let mut wires = HashSet::with_hasher(egui::ahash::RandomState::new());
 | 
				
			||||||
 | 
					                while let Some(wire) = seq.next_element()? {
 | 
				
			||||||
 | 
					                    wires.insert(wire);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                Ok(wires)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let wires = deserializer.deserialize_seq(Visitor)?;
 | 
				
			||||||
 | 
					        Ok(Wires { wires })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Wires {
 | 
				
			||||||
 | 
					    fn new() -> Self {
 | 
				
			||||||
 | 
					        Wires {
 | 
				
			||||||
 | 
					            wires: HashSet::with_hasher(egui::ahash::RandomState::new()),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn insert(&mut self, wire: Wire) -> bool {
 | 
				
			||||||
 | 
					        self.wires.insert(wire)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn remove(&mut self, wire: &Wire) -> bool {
 | 
				
			||||||
 | 
					        self.wires.remove(wire)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn drop_node(&mut self, node: NodeId) -> usize {
 | 
				
			||||||
 | 
					        let count = self.wires.len();
 | 
				
			||||||
 | 
					        self.wires
 | 
				
			||||||
 | 
					            .retain(|wire| wire.out_pin.node != node && wire.in_pin.node != node);
 | 
				
			||||||
 | 
					        count - self.wires.len()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn drop_inputs(&mut self, pin: InPinId) -> usize {
 | 
				
			||||||
 | 
					        let count = self.wires.len();
 | 
				
			||||||
 | 
					        self.wires.retain(|wire| wire.in_pin != pin);
 | 
				
			||||||
 | 
					        count - self.wires.len()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn drop_outputs(&mut self, pin: OutPinId) -> usize {
 | 
				
			||||||
 | 
					        let count = self.wires.len();
 | 
				
			||||||
 | 
					        self.wires.retain(|wire| wire.out_pin != pin);
 | 
				
			||||||
 | 
					        count - self.wires.len()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn wired_inputs(&self, out_pin: OutPinId) -> impl Iterator<Item = InPinId> + '_ {
 | 
				
			||||||
 | 
					        self.wires
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .filter(move |wire| wire.out_pin == out_pin)
 | 
				
			||||||
 | 
					            .map(|wire| (wire.in_pin))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn wired_outputs(&self, in_pin: InPinId) -> impl Iterator<Item = OutPinId> + '_ {
 | 
				
			||||||
 | 
					        self.wires
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .filter(move |wire| wire.in_pin == in_pin)
 | 
				
			||||||
 | 
					            .map(|wire| (wire.out_pin))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn iter(&self) -> impl Iterator<Item = Wire> + '_ {
 | 
				
			||||||
 | 
					        self.wires.iter().copied()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Snarl is generic node-graph container.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// It holds graph state - positioned nodes and wires between their pins.
 | 
				
			||||||
 | 
					/// It can be rendered using [`Snarl::show`].
 | 
				
			||||||
 | 
					#[derive(Clone, Debug)]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
 | 
					pub struct Snarl<T> {
 | 
				
			||||||
 | 
					    // #[cfg_attr(feature = "serde", serde(with = "serde_nodes"))]
 | 
				
			||||||
 | 
					    nodes: Slab<Node<T>>,
 | 
				
			||||||
 | 
					    draw_order: Vec<NodeId>,
 | 
				
			||||||
 | 
					    wires: Wires,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> Snarl<T> {
 | 
				
			||||||
 | 
					    /// Create a new empty Snarl.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Examples
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// ```
 | 
				
			||||||
 | 
					    /// # use egui_snarl::Snarl;
 | 
				
			||||||
 | 
					    /// let snarl = Snarl::<()>::new();
 | 
				
			||||||
 | 
					    /// ```
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Snarl {
 | 
				
			||||||
 | 
					            nodes: Slab::new(),
 | 
				
			||||||
 | 
					            draw_order: Vec::new(),
 | 
				
			||||||
 | 
					            wires: Wires::new(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Adds a node to the Snarl.
 | 
				
			||||||
 | 
					    /// Returns the index of the node.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Examples
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// ```
 | 
				
			||||||
 | 
					    /// # use egui_snarl::Snarl;
 | 
				
			||||||
 | 
					    /// let mut snarl = Snarl::<()>::new();
 | 
				
			||||||
 | 
					    /// snarl.insert_node(egui::pos2(0.0, 0.0), ());
 | 
				
			||||||
 | 
					    /// ```
 | 
				
			||||||
 | 
					    pub fn insert_node(&mut self, pos: egui::Pos2, node: T) -> NodeId {
 | 
				
			||||||
 | 
					        let idx = self.nodes.insert(Node {
 | 
				
			||||||
 | 
					            value: node,
 | 
				
			||||||
 | 
					            pos,
 | 
				
			||||||
 | 
					            open: true,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        let id = NodeId(idx);
 | 
				
			||||||
 | 
					        self.draw_order.push(id);
 | 
				
			||||||
 | 
					        id
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Adds a node to the Snarl in collapsed state.
 | 
				
			||||||
 | 
					    /// Returns the index of the node.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Examples
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// ```
 | 
				
			||||||
 | 
					    /// # use egui_snarl::Snarl;
 | 
				
			||||||
 | 
					    /// let mut snarl = Snarl::<()>::new();
 | 
				
			||||||
 | 
					    /// snarl.insert_node_collapsed(egui::pos2(0.0, 0.0), ());
 | 
				
			||||||
 | 
					    /// ```
 | 
				
			||||||
 | 
					    pub fn insert_node_collapsed(&mut self, pos: egui::Pos2, node: T) -> NodeId {
 | 
				
			||||||
 | 
					        let idx = self.nodes.insert(Node {
 | 
				
			||||||
 | 
					            value: node,
 | 
				
			||||||
 | 
					            pos,
 | 
				
			||||||
 | 
					            open: false,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        let id = NodeId(idx);
 | 
				
			||||||
 | 
					        self.draw_order.push(id);
 | 
				
			||||||
 | 
					        id
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Opens or collapses a node.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Panics
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Panics if the node does not exist.
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    pub fn open_node(&mut self, node: NodeId, open: bool) {
 | 
				
			||||||
 | 
					        self.nodes[node.0].open = open;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Removes a node from the Snarl.
 | 
				
			||||||
 | 
					    /// Returns the node if it was removed.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Panics
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Panics if the node does not exist.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Examples
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// ```
 | 
				
			||||||
 | 
					    /// # use egui_snarl::Snarl;
 | 
				
			||||||
 | 
					    /// let mut snarl = Snarl::<()>::new();
 | 
				
			||||||
 | 
					    /// let node = snarl.insert_node(egui::pos2(0.0, 0.0), ());
 | 
				
			||||||
 | 
					    /// snarl.remove_node(node);
 | 
				
			||||||
 | 
					    /// ```
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    pub fn remove_node(&mut self, idx: NodeId) -> T {
 | 
				
			||||||
 | 
					        let value = self.nodes.remove(idx.0).value;
 | 
				
			||||||
 | 
					        self.wires.drop_node(idx);
 | 
				
			||||||
 | 
					        let order = self.draw_order.iter().position(|&i| i == idx).unwrap();
 | 
				
			||||||
 | 
					        self.draw_order.remove(order);
 | 
				
			||||||
 | 
					        value
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Connects two nodes.
 | 
				
			||||||
 | 
					    /// Returns true if the connection was successful.
 | 
				
			||||||
 | 
					    /// Returns false if the connection already exists.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Panics
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Panics if either node does not exist.
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    pub fn connect(&mut self, from: OutPinId, to: InPinId) -> bool {
 | 
				
			||||||
 | 
					        assert!(self.nodes.contains(from.node.0));
 | 
				
			||||||
 | 
					        assert!(self.nodes.contains(to.node.0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let wire = Wire {
 | 
				
			||||||
 | 
					            out_pin: from,
 | 
				
			||||||
 | 
					            in_pin: to,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        self.wires.insert(wire)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Disconnects two nodes.
 | 
				
			||||||
 | 
					    /// Returns true if the connection was removed.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Panics
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Panics if either node does not exist.
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    pub fn disconnect(&mut self, from: OutPinId, to: InPinId) -> bool {
 | 
				
			||||||
 | 
					        assert!(self.nodes.contains(from.node.0));
 | 
				
			||||||
 | 
					        assert!(self.nodes.contains(to.node.0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let wire = Wire {
 | 
				
			||||||
 | 
					            out_pin: from,
 | 
				
			||||||
 | 
					            in_pin: to,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wires.remove(&wire)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Removes all connections to the node's pin.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns number of removed connections.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Panics
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Panics if the node does not exist.
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    pub fn drop_inputs(&mut self, pin: InPinId) -> usize {
 | 
				
			||||||
 | 
					        assert!(self.nodes.contains(pin.node.0));
 | 
				
			||||||
 | 
					        self.wires.drop_inputs(pin)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Removes all connections from the node's pin.
 | 
				
			||||||
 | 
					    /// Returns number of removed connections.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Panics
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Panics if the node does not exist.
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    pub fn drop_outputs(&mut self, pin: OutPinId) -> usize {
 | 
				
			||||||
 | 
					        assert!(self.nodes.contains(pin.node.0));
 | 
				
			||||||
 | 
					        self.wires.drop_outputs(pin)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns reference to the node.
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn get_node(&self, idx: NodeId) -> Option<&T> {
 | 
				
			||||||
 | 
					        match self.nodes.get(idx.0) {
 | 
				
			||||||
 | 
					            Some(node) => Some(&node.value),
 | 
				
			||||||
 | 
					            None => None,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns mutable reference to the node.
 | 
				
			||||||
 | 
					    pub fn get_node_mut(&mut self, idx: NodeId) -> Option<&mut T> {
 | 
				
			||||||
 | 
					        match self.nodes.get_mut(idx.0) {
 | 
				
			||||||
 | 
					            Some(node) => Some(&mut node.value),
 | 
				
			||||||
 | 
					            None => None,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Iterates over shared references to each node.
 | 
				
			||||||
 | 
					    pub fn nodes(&self) -> NodesIter<'_, T> {
 | 
				
			||||||
 | 
					        NodesIter {
 | 
				
			||||||
 | 
					            nodes: self.nodes.iter(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Iterates over mutable references to each node.
 | 
				
			||||||
 | 
					    pub fn nodes_mut(&mut self) -> NodesIterMut<'_, T> {
 | 
				
			||||||
 | 
					        NodesIterMut {
 | 
				
			||||||
 | 
					            nodes: self.nodes.iter_mut(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Iterates over shared references to each node and its position.
 | 
				
			||||||
 | 
					    pub fn nodes_pos(&self) -> NodesPosIter<'_, T> {
 | 
				
			||||||
 | 
					        NodesPosIter {
 | 
				
			||||||
 | 
					            nodes: self.nodes.iter(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Iterates over mutable references to each node and its position.
 | 
				
			||||||
 | 
					    pub fn nodes_pos_mut(&mut self) -> NodesPosIterMut<'_, T> {
 | 
				
			||||||
 | 
					        NodesPosIterMut {
 | 
				
			||||||
 | 
					            nodes: self.nodes.iter_mut(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Iterates over shared references to each node and its identifier.
 | 
				
			||||||
 | 
					    pub fn node_ids(&self) -> NodesIdsIter<'_, T> {
 | 
				
			||||||
 | 
					        NodesIdsIter {
 | 
				
			||||||
 | 
					            nodes: self.nodes.iter(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Iterates over mutable references to each node and its identifier.
 | 
				
			||||||
 | 
					    pub fn nodes_ids_mut(&mut self) -> NodesIdsIterMut<'_, T> {
 | 
				
			||||||
 | 
					        NodesIdsIterMut {
 | 
				
			||||||
 | 
					            nodes: self.nodes.iter_mut(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Iterates over shared references to each node, its position and its identifier.
 | 
				
			||||||
 | 
					    pub fn nodes_pos_ids(&self) -> NodesPosIdsIter<'_, T> {
 | 
				
			||||||
 | 
					        NodesPosIdsIter {
 | 
				
			||||||
 | 
					            nodes: self.nodes.iter(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Iterates over mutable references to each node, its position and its identifier.
 | 
				
			||||||
 | 
					    pub fn nodes_pos_ids_mut(&mut self) -> NodesPosIdsIterMut<'_, T> {
 | 
				
			||||||
 | 
					        NodesPosIdsIterMut {
 | 
				
			||||||
 | 
					            nodes: self.nodes.iter_mut(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns input pin of the node.
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn in_pin(&self, pin: InPinId) -> InPin {
 | 
				
			||||||
 | 
					        InPin::new(self, pin)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns output pin of the node.
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn out_pin(&self, pin: OutPinId) -> OutPin {
 | 
				
			||||||
 | 
					        OutPin::new(self, pin)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> Index<NodeId> for Snarl<T> {
 | 
				
			||||||
 | 
					    type Output = T;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline]
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    fn index(&self, idx: NodeId) -> &Self::Output {
 | 
				
			||||||
 | 
					        &self.nodes[idx.0].value
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> IndexMut<NodeId> for Snarl<T> {
 | 
				
			||||||
 | 
					    #[inline]
 | 
				
			||||||
 | 
					    #[track_caller]
 | 
				
			||||||
 | 
					    fn index_mut(&mut self, idx: NodeId) -> &mut Self::Output {
 | 
				
			||||||
 | 
					        &mut self.nodes[idx.0].value
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Iterator over shared references to nodes.
 | 
				
			||||||
 | 
					#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 | 
				
			||||||
 | 
					pub struct NodesIter<'a, T> {
 | 
				
			||||||
 | 
					    nodes: slab::Iter<'a, Node<T>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a, T> Iterator for NodesIter<'a, T> {
 | 
				
			||||||
 | 
					    type Item = &'a T;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn size_hint(&self) -> (usize, Option<usize>) {
 | 
				
			||||||
 | 
					        self.nodes.size_hint()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn next(&mut self) -> Option<&'a T> {
 | 
				
			||||||
 | 
					        let (_, node) = self.nodes.next()?;
 | 
				
			||||||
 | 
					        Some(&node.value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn nth(&mut self, n: usize) -> Option<&'a T> {
 | 
				
			||||||
 | 
					        let (_, node) = self.nodes.nth(n)?;
 | 
				
			||||||
 | 
					        Some(&node.value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Iterator over mutable references to nodes.
 | 
				
			||||||
 | 
					#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 | 
				
			||||||
 | 
					pub struct NodesIterMut<'a, T> {
 | 
				
			||||||
 | 
					    nodes: slab::IterMut<'a, Node<T>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a, T> Iterator for NodesIterMut<'a, T> {
 | 
				
			||||||
 | 
					    type Item = &'a mut T;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn size_hint(&self) -> (usize, Option<usize>) {
 | 
				
			||||||
 | 
					        self.nodes.size_hint()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn next(&mut self) -> Option<&'a mut T> {
 | 
				
			||||||
 | 
					        let (_, node) = self.nodes.next()?;
 | 
				
			||||||
 | 
					        Some(&mut node.value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn nth(&mut self, n: usize) -> Option<&'a mut T> {
 | 
				
			||||||
 | 
					        let (_, node) = self.nodes.nth(n)?;
 | 
				
			||||||
 | 
					        Some(&mut node.value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Iterator over shared references to nodes and their positions.
 | 
				
			||||||
 | 
					#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 | 
				
			||||||
 | 
					pub struct NodesPosIter<'a, T> {
 | 
				
			||||||
 | 
					    nodes: slab::Iter<'a, Node<T>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a, T> Iterator for NodesPosIter<'a, T> {
 | 
				
			||||||
 | 
					    type Item = (Pos2, &'a T);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn size_hint(&self) -> (usize, Option<usize>) {
 | 
				
			||||||
 | 
					        self.nodes.size_hint()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn next(&mut self) -> Option<(Pos2, &'a T)> {
 | 
				
			||||||
 | 
					        let (_, node) = self.nodes.next()?;
 | 
				
			||||||
 | 
					        Some((node.pos, &node.value))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn nth(&mut self, n: usize) -> Option<(Pos2, &'a T)> {
 | 
				
			||||||
 | 
					        let (_, node) = self.nodes.nth(n)?;
 | 
				
			||||||
 | 
					        Some((node.pos, &node.value))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Iterator over mutable references to nodes and their positions.
 | 
				
			||||||
 | 
					#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 | 
				
			||||||
 | 
					pub struct NodesPosIterMut<'a, T> {
 | 
				
			||||||
 | 
					    nodes: slab::IterMut<'a, Node<T>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a, T> Iterator for NodesPosIterMut<'a, T> {
 | 
				
			||||||
 | 
					    type Item = (Pos2, &'a mut T);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn size_hint(&self) -> (usize, Option<usize>) {
 | 
				
			||||||
 | 
					        self.nodes.size_hint()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn next(&mut self) -> Option<(Pos2, &'a mut T)> {
 | 
				
			||||||
 | 
					        let (_, node) = self.nodes.next()?;
 | 
				
			||||||
 | 
					        Some((node.pos, &mut node.value))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn nth(&mut self, n: usize) -> Option<(Pos2, &'a mut T)> {
 | 
				
			||||||
 | 
					        let (_, node) = self.nodes.nth(n)?;
 | 
				
			||||||
 | 
					        Some((node.pos, &mut node.value))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Iterator over shared references to nodes and their identifiers.
 | 
				
			||||||
 | 
					#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 | 
				
			||||||
 | 
					pub struct NodesIdsIter<'a, T> {
 | 
				
			||||||
 | 
					    nodes: slab::Iter<'a, Node<T>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a, T> Iterator for NodesIdsIter<'a, T> {
 | 
				
			||||||
 | 
					    type Item = (NodeId, &'a T);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn size_hint(&self) -> (usize, Option<usize>) {
 | 
				
			||||||
 | 
					        self.nodes.size_hint()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn next(&mut self) -> Option<(NodeId, &'a T)> {
 | 
				
			||||||
 | 
					        let (idx, node) = self.nodes.next()?;
 | 
				
			||||||
 | 
					        Some((NodeId(idx), &node.value))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn nth(&mut self, n: usize) -> Option<(NodeId, &'a T)> {
 | 
				
			||||||
 | 
					        let (idx, node) = self.nodes.nth(n)?;
 | 
				
			||||||
 | 
					        Some((NodeId(idx), &node.value))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Iterator over mutable references to nodes and their identifiers.
 | 
				
			||||||
 | 
					#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 | 
				
			||||||
 | 
					pub struct NodesIdsIterMut<'a, T> {
 | 
				
			||||||
 | 
					    nodes: slab::IterMut<'a, Node<T>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a, T> Iterator for NodesIdsIterMut<'a, T> {
 | 
				
			||||||
 | 
					    type Item = (NodeId, &'a mut T);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn size_hint(&self) -> (usize, Option<usize>) {
 | 
				
			||||||
 | 
					        self.nodes.size_hint()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn next(&mut self) -> Option<(NodeId, &'a mut T)> {
 | 
				
			||||||
 | 
					        let (idx, node) = self.nodes.next()?;
 | 
				
			||||||
 | 
					        Some((NodeId(idx), &mut node.value))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn nth(&mut self, n: usize) -> Option<(NodeId, &'a mut T)> {
 | 
				
			||||||
 | 
					        let (idx, node) = self.nodes.nth(n)?;
 | 
				
			||||||
 | 
					        Some((NodeId(idx), &mut node.value))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Iterator over shared references to nodes, their positions and their identifiers.
 | 
				
			||||||
 | 
					#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 | 
				
			||||||
 | 
					pub struct NodesPosIdsIter<'a, T> {
 | 
				
			||||||
 | 
					    nodes: slab::Iter<'a, Node<T>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a, T> Iterator for NodesPosIdsIter<'a, T> {
 | 
				
			||||||
 | 
					    type Item = (NodeId, Pos2, &'a T);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn size_hint(&self) -> (usize, Option<usize>) {
 | 
				
			||||||
 | 
					        self.nodes.size_hint()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn next(&mut self) -> Option<(NodeId, Pos2, &'a T)> {
 | 
				
			||||||
 | 
					        let (idx, node) = self.nodes.next()?;
 | 
				
			||||||
 | 
					        Some((NodeId(idx), node.pos, &node.value))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn nth(&mut self, n: usize) -> Option<(NodeId, Pos2, &'a T)> {
 | 
				
			||||||
 | 
					        let (idx, node) = self.nodes.nth(n)?;
 | 
				
			||||||
 | 
					        Some((NodeId(idx), node.pos, &node.value))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Iterator over mutable references to nodes, their positions and their identifiers.
 | 
				
			||||||
 | 
					#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 | 
				
			||||||
 | 
					pub struct NodesPosIdsIterMut<'a, T> {
 | 
				
			||||||
 | 
					    nodes: slab::IterMut<'a, Node<T>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a, T> Iterator for NodesPosIdsIterMut<'a, T> {
 | 
				
			||||||
 | 
					    type Item = (NodeId, Pos2, &'a mut T);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn size_hint(&self) -> (usize, Option<usize>) {
 | 
				
			||||||
 | 
					        self.nodes.size_hint()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn next(&mut self) -> Option<(NodeId, Pos2, &'a mut T)> {
 | 
				
			||||||
 | 
					        let (idx, node) = self.nodes.next()?;
 | 
				
			||||||
 | 
					        Some((NodeId(idx), node.pos, &mut node.value))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn nth(&mut self, n: usize) -> Option<(NodeId, Pos2, &'a mut T)> {
 | 
				
			||||||
 | 
					        let (idx, node) = self.nodes.nth(n)?;
 | 
				
			||||||
 | 
					        Some((NodeId(idx), node.pos, &mut node.value))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Node and its output pin.
 | 
				
			||||||
 | 
					#[derive(Clone, Debug)]
 | 
				
			||||||
 | 
					pub struct OutPin {
 | 
				
			||||||
 | 
					    /// Output pin identifier.
 | 
				
			||||||
 | 
					    pub id: OutPinId,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// List of input pins connected to this output pin.
 | 
				
			||||||
 | 
					    pub remotes: Vec<InPinId>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Node and its output pin.
 | 
				
			||||||
 | 
					#[derive(Clone, Debug)]
 | 
				
			||||||
 | 
					pub struct InPin {
 | 
				
			||||||
 | 
					    /// Input pin identifier.
 | 
				
			||||||
 | 
					    pub id: InPinId,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// List of output pins connected to this input pin.
 | 
				
			||||||
 | 
					    pub remotes: Vec<OutPinId>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl OutPin {
 | 
				
			||||||
 | 
					    fn new<T>(snarl: &Snarl<T>, pin: OutPinId) -> Self {
 | 
				
			||||||
 | 
					        OutPin {
 | 
				
			||||||
 | 
					            id: pin,
 | 
				
			||||||
 | 
					            remotes: snarl.wires.wired_inputs(pin).collect(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl InPin {
 | 
				
			||||||
 | 
					    fn new<T>(snarl: &Snarl<T>, pin: InPinId) -> Self {
 | 
				
			||||||
 | 
					        InPin {
 | 
				
			||||||
 | 
					            id: pin,
 | 
				
			||||||
 | 
					            remotes: snarl.wires.wired_outputs(pin).collect(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1089
									
								
								vendor/egui-snarl/src/ui.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1089
									
								
								vendor/egui-snarl/src/ui.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										259
									
								
								vendor/egui-snarl/src/ui/background_pattern.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								vendor/egui-snarl/src/ui/background_pattern.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,259 @@
 | 
				
			||||||
 | 
					use std::fmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use egui::{emath::Rot2, vec2, Pos2, Rect, Stroke, Ui, Vec2};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::SnarlStyle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Viewport is a rectangle in graph space that is visible on screen.
 | 
				
			||||||
 | 
					pub struct Viewport {
 | 
				
			||||||
 | 
					    /// Screen-space rectangle.
 | 
				
			||||||
 | 
					    pub rect: Rect,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Scale of the viewport.
 | 
				
			||||||
 | 
					    pub scale: f32,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Offset of the viewport.
 | 
				
			||||||
 | 
					    pub offset: Vec2,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Viewport {
 | 
				
			||||||
 | 
					    /// Converts screen-space position to graph-space position.
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn screen_pos_to_graph(&self, pos: Pos2) -> Pos2 {
 | 
				
			||||||
 | 
					        (pos + self.offset - self.rect.center().to_vec2()) / self.scale
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Converts graph-space position to screen-space position.
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn graph_pos_to_screen(&self, pos: Pos2) -> Pos2 {
 | 
				
			||||||
 | 
					        pos * self.scale - self.offset + self.rect.center().to_vec2()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Converts screen-space vector to graph-space vector.
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn graph_vec_to_screen(&self, size: Vec2) -> Vec2 {
 | 
				
			||||||
 | 
					        size * self.scale
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Converts graph-space vector to screen-space vector.
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn screen_vec_to_graph(&self, size: Vec2) -> Vec2 {
 | 
				
			||||||
 | 
					        size / self.scale
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Converts screen-space size to graph-space size.
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn graph_size_to_screen(&self, size: f32) -> f32 {
 | 
				
			||||||
 | 
					        size * self.scale
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Converts graph-space size to screen-space size.
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn screen_size_to_graph(&self, size: f32) -> f32 {
 | 
				
			||||||
 | 
					        size / self.scale
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					///Grid background pattern.
 | 
				
			||||||
 | 
					///Use `SnarlStyle::background_pattern_stroke` for change stroke options
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, Debug, PartialEq)]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "egui-probe", derive(egui_probe::EguiProbe))]
 | 
				
			||||||
 | 
					pub struct Grid {
 | 
				
			||||||
 | 
					    /// Spacing between grid lines.
 | 
				
			||||||
 | 
					    pub spacing: Vec2,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Angle of the grid.
 | 
				
			||||||
 | 
					    #[cfg_attr(feature = "egui-probe", egui_probe(as egui_probe::angle))]
 | 
				
			||||||
 | 
					    pub angle: f32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DEFAULT_GRID_SPACING: Vec2 = vec2(5.0, 5.0);
 | 
				
			||||||
 | 
					macro_rules! default_grid_spacing {
 | 
				
			||||||
 | 
					    () => {
 | 
				
			||||||
 | 
					        stringify!(vec2(5.0, 5.0))
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DEFAULT_GRID_ANGLE: f32 = 1.0;
 | 
				
			||||||
 | 
					macro_rules! default_grid_angle {
 | 
				
			||||||
 | 
					    () => {
 | 
				
			||||||
 | 
					        stringify!(1.0)
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for Grid {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            spacing: DEFAULT_GRID_SPACING,
 | 
				
			||||||
 | 
					            angle: DEFAULT_GRID_ANGLE,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Grid {
 | 
				
			||||||
 | 
					    /// Create new grid with given spacing and angle.
 | 
				
			||||||
 | 
					    pub const fn new(spacing: Vec2, angle: f32) -> Self {
 | 
				
			||||||
 | 
					        Self { spacing, angle }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn draw(&self, style: &SnarlStyle, viewport: &Viewport, ui: &mut Ui) {
 | 
				
			||||||
 | 
					        let bg_stroke = style
 | 
				
			||||||
 | 
					            .background_pattern_stroke
 | 
				
			||||||
 | 
					            .unwrap_or_else(|| ui.visuals().widgets.noninteractive.bg_stroke);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let stroke = Stroke::new(
 | 
				
			||||||
 | 
					            bg_stroke.width * viewport.scale.max(1.0),
 | 
				
			||||||
 | 
					            bg_stroke.color.gamma_multiply(viewport.scale.min(1.0)),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let spacing =
 | 
				
			||||||
 | 
					            ui.spacing().icon_width * vec2(self.spacing.x.max(1.0), self.spacing.y.max(1.0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let rot = Rot2::from_angle(self.angle);
 | 
				
			||||||
 | 
					        let rot_inv = rot.inverse();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let graph_viewport = Rect::from_min_max(
 | 
				
			||||||
 | 
					            viewport.screen_pos_to_graph(viewport.rect.min),
 | 
				
			||||||
 | 
					            viewport.screen_pos_to_graph(viewport.rect.max),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let pattern_bounds = graph_viewport.rotate_bb(rot_inv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let min_x = (pattern_bounds.min.x / spacing.x).ceil();
 | 
				
			||||||
 | 
					        let max_x = (pattern_bounds.max.x / spacing.x).floor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for x in 0..=(max_x - min_x) as i64 {
 | 
				
			||||||
 | 
					            #[allow(clippy::cast_possible_truncation)]
 | 
				
			||||||
 | 
					            let x = (x as f32 + min_x) * spacing.x;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let top = (rot * vec2(x, pattern_bounds.min.y)).to_pos2();
 | 
				
			||||||
 | 
					            let bottom = (rot * vec2(x, pattern_bounds.max.y)).to_pos2();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let top = viewport.graph_pos_to_screen(top);
 | 
				
			||||||
 | 
					            let bottom = viewport.graph_pos_to_screen(bottom);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ui.painter().line_segment([top, bottom], stroke);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let min_y = (pattern_bounds.min.y / spacing.y).ceil();
 | 
				
			||||||
 | 
					        let max_y = (pattern_bounds.max.y / spacing.y).floor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for y in 0..=(max_y - min_y) as i64 {
 | 
				
			||||||
 | 
					            #[allow(clippy::cast_possible_truncation)]
 | 
				
			||||||
 | 
					            let y = (y as f32 + min_y) * spacing.y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let top = (rot * vec2(pattern_bounds.min.x, y)).to_pos2();
 | 
				
			||||||
 | 
					            let bottom = (rot * vec2(pattern_bounds.max.x, y)).to_pos2();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let top = viewport.graph_pos_to_screen(top);
 | 
				
			||||||
 | 
					            let bottom = viewport.graph_pos_to_screen(bottom);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ui.painter().line_segment([top, bottom], stroke);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tiny_fn::tiny_fn! {
 | 
				
			||||||
 | 
					    /// Custom background pattern function with signature
 | 
				
			||||||
 | 
					    /// `Fn(style: &SnarlStyle, viewport: &Viewport, ui: &mut Ui)`
 | 
				
			||||||
 | 
					    pub struct CustomBackground = Fn(style: &SnarlStyle, viewport: &Viewport, ui: &mut Ui);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<const INLINE_SIZE: usize> Default for CustomBackground<'_, INLINE_SIZE> {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self::new(|_, _, _| {})
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "egui-probe")]
 | 
				
			||||||
 | 
					impl<const INLINE_SIZE: usize> egui_probe::EguiProbe for CustomBackground<'_, INLINE_SIZE> {
 | 
				
			||||||
 | 
					    fn probe(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        ui: &mut egui_probe::egui::Ui,
 | 
				
			||||||
 | 
					        _style: &egui_probe::Style,
 | 
				
			||||||
 | 
					    ) -> egui_probe::egui::Response {
 | 
				
			||||||
 | 
					        ui.weak("Custom")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Background pattern show beneath nodes and wires.
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "egui-probe", derive(egui_probe::EguiProbe))]
 | 
				
			||||||
 | 
					pub enum BackgroundPattern {
 | 
				
			||||||
 | 
					    /// No pattern.
 | 
				
			||||||
 | 
					    NoPattern,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Linear grid.
 | 
				
			||||||
 | 
					    #[cfg_attr(feature = "egui-probe", egui_probe(transparent))]
 | 
				
			||||||
 | 
					    Grid(Grid),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Custom pattern.
 | 
				
			||||||
 | 
					    /// Contains function with signature
 | 
				
			||||||
 | 
					    /// `Fn(style: &SnarlStyle, viewport: &Viewport, ui: &mut Ui)`
 | 
				
			||||||
 | 
					    #[cfg_attr(feature = "egui-probe", egui_probe(transparent))]
 | 
				
			||||||
 | 
					    Custom(#[cfg_attr(feature = "serde", serde(skip))] CustomBackground<'static>),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl PartialEq for BackgroundPattern {
 | 
				
			||||||
 | 
					    fn eq(&self, other: &Self) -> bool {
 | 
				
			||||||
 | 
					        match (self, other) {
 | 
				
			||||||
 | 
					            (BackgroundPattern::Grid(l), BackgroundPattern::Grid(r)) => *l == *r,
 | 
				
			||||||
 | 
					            _ => false,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Debug for BackgroundPattern {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            BackgroundPattern::Grid(grid) => f
 | 
				
			||||||
 | 
					                .debug_tuple("BackgroundPattern::Grid")
 | 
				
			||||||
 | 
					                .field(grid)
 | 
				
			||||||
 | 
					                .finish(),
 | 
				
			||||||
 | 
					            BackgroundPattern::Custom(_) => f.write_str("BackgroundPattern::Custom"),
 | 
				
			||||||
 | 
					            BackgroundPattern::NoPattern => f.write_str("BackgroundPattern::NoPattern"),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for BackgroundPattern {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self::Grid(Default::default())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl BackgroundPattern {
 | 
				
			||||||
 | 
					    /// Create new background pattern with default values.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Default patter is `Grid` with spacing - `
 | 
				
			||||||
 | 
					    #[doc = default_grid_spacing!()]
 | 
				
			||||||
 | 
					    /// ` and angle - `
 | 
				
			||||||
 | 
					    #[doc = default_grid_angle!()]
 | 
				
			||||||
 | 
					    /// ` radian.
 | 
				
			||||||
 | 
					    pub const fn new() -> Self {
 | 
				
			||||||
 | 
					        Self::Grid(Grid::new(DEFAULT_GRID_SPACING, DEFAULT_GRID_ANGLE))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Create new grid background pattern with given spacing and angle.
 | 
				
			||||||
 | 
					    pub const fn grid(spacing: Vec2, angle: f32) -> Self {
 | 
				
			||||||
 | 
					        Self::Grid(Grid::new(spacing, angle))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Create new custom background pattern.
 | 
				
			||||||
 | 
					    pub fn custom<F>(f: F) -> Self
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        F: Fn(&SnarlStyle, &Viewport, &mut Ui) + 'static,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Self::Custom(CustomBackground::new(f))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Draws background pattern.
 | 
				
			||||||
 | 
					    pub(super) fn draw(&self, style: &SnarlStyle, viewport: &Viewport, ui: &mut Ui) {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            BackgroundPattern::Grid(g) => g.draw(style, viewport, ui),
 | 
				
			||||||
 | 
					            BackgroundPattern::Custom(c) => c.call(style, viewport, ui),
 | 
				
			||||||
 | 
					            BackgroundPattern::NoPattern => {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										160
									
								
								vendor/egui-snarl/src/ui/effect.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								vendor/egui-snarl/src/ui/effect.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,160 @@
 | 
				
			||||||
 | 
					use std::cell::RefCell;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use egui::Pos2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{wire_pins, InPinId, Node, OutPinId, Snarl};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub enum Effect<T> {
 | 
				
			||||||
 | 
					    /// Adds a new node to the Snarl.
 | 
				
			||||||
 | 
					    InsertNode { pos: Pos2, node: T },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Removes a node from snarl.
 | 
				
			||||||
 | 
					    RemoveNode { node: NodeId },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Opens/closes a node.
 | 
				
			||||||
 | 
					    OpenNode { node: NodeId, open: bool },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Adds connection between two nodes.
 | 
				
			||||||
 | 
					    Connect { from: OutPinId, to: InPinId },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Removes connection between two nodes.
 | 
				
			||||||
 | 
					    Disconnect { from: OutPinId, to: InPinId },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Removes all connections from the output pin.
 | 
				
			||||||
 | 
					    DropOutputs { pin: OutPinId },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Removes all connections to the input pin.
 | 
				
			||||||
 | 
					    DropInputs { pin: InPinId },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Executes a closure with mutable reference to the Snarl.
 | 
				
			||||||
 | 
					    Closure(Box<dyn FnOnce(&mut Snarl<T>)>),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Contained for deferred execution of effects.
 | 
				
			||||||
 | 
					/// It is populated by [`SnarlViewer`] methods and then applied to the Snarl.
 | 
				
			||||||
 | 
					pub struct Effects<T> {
 | 
				
			||||||
 | 
					    effects: Vec<Effect<T>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> Default for Effects<T> {
 | 
				
			||||||
 | 
					    #[inline]
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Effects {
 | 
				
			||||||
 | 
					            effects: Default::default(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> Effects<T> {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    #[doc(hidden)]
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Effects {
 | 
				
			||||||
 | 
					            effects: Vec::new(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns `true` if there are no effects.
 | 
				
			||||||
 | 
					    /// Returns `false` otherwise.
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn is_empty(&self) -> bool {
 | 
				
			||||||
 | 
					        self.effects.is_empty()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Inserts a new node to the Snarl.
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn insert_node(&mut self, pos: Pos2, node: T) {
 | 
				
			||||||
 | 
					        self.effects.push(Effect::InsertNode { node, pos });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Removes a node from the Snarl.
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn remove_node(&mut self, node: NodeId) {
 | 
				
			||||||
 | 
					        self.effects.push(Effect::RemoveNode { node });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Opens/closes a node.
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn open_node(&mut self, node: NodeId, open: bool) {
 | 
				
			||||||
 | 
					        self.effects.push(Effect::OpenNode { node, open });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Connects two nodes.
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn connect(&mut self, from: OutPinId, to: InPinId) {
 | 
				
			||||||
 | 
					        self.effects.push(Effect::Connect { from, to });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Disconnects two nodes.
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn disconnect(&mut self, from: OutPinId, to: InPinId) {
 | 
				
			||||||
 | 
					        self.effects.push(Effect::Disconnect { from, to });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Removes all connections from the output pin.
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn drop_inputs(&mut self, pin: InPinId) {
 | 
				
			||||||
 | 
					        self.effects.push(Effect::DropInputs { pin });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Removes all connections to the input pin.
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn drop_outputs(&mut self, pin: OutPinId) {
 | 
				
			||||||
 | 
					        self.effects.push(Effect::DropOutputs { pin });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> Snarl<T> {
 | 
				
			||||||
 | 
					    pub fn apply_effects(&mut self, effects: Effects<T>) {
 | 
				
			||||||
 | 
					        if effects.effects.is_empty() {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for effect in effects.effects {
 | 
				
			||||||
 | 
					            self.apply_effect(effect);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn apply_effect(&mut self, effect: Effect<T>) {
 | 
				
			||||||
 | 
					        match effect {
 | 
				
			||||||
 | 
					            Effect::InsertNode { node, pos } => {
 | 
				
			||||||
 | 
					                let idx = self.nodes.insert(Node {
 | 
				
			||||||
 | 
					                    value: RefCell::new(node),
 | 
				
			||||||
 | 
					                    pos,
 | 
				
			||||||
 | 
					                    open: true,
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                self.draw_order.push(idx);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Effect::RemoveNode { node } => {
 | 
				
			||||||
 | 
					                if self.nodes.contains(node) {
 | 
				
			||||||
 | 
					                    self.remove_node(node);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Effect::OpenNode { node, open } => {
 | 
				
			||||||
 | 
					                if self.nodes.contains(node) {
 | 
				
			||||||
 | 
					                    self.nodes[node].open = open;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Effect::Connect { from, to } => {
 | 
				
			||||||
 | 
					                if self.nodes.contains(from.node) && self.nodes.contains(to.node) {
 | 
				
			||||||
 | 
					                    self.wires.insert(wire_pins(from, to));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Effect::Disconnect { from, to } => {
 | 
				
			||||||
 | 
					                if self.nodes.contains(from.node) && self.nodes.contains(to.node) {
 | 
				
			||||||
 | 
					                    self.wires.remove(&wire_pins(from, to));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Effect::DropOutputs { pin } => {
 | 
				
			||||||
 | 
					                if self.nodes.contains(pin.node) {
 | 
				
			||||||
 | 
					                    self.wires.drop_outputs(pin);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Effect::DropInputs { pin } => {
 | 
				
			||||||
 | 
					                if self.nodes.contains(pin.node) {
 | 
				
			||||||
 | 
					                    self.wires.drop_inputs(pin);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Effect::Closure(f) => f(self),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										171
									
								
								vendor/egui-snarl/src/ui/pin.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								vendor/egui-snarl/src/ui/pin.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,171 @@
 | 
				
			||||||
 | 
					use egui::{epaint::PathShape, vec2, Color32, Painter, Pos2, Rect, Shape, Stroke, Vec2};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{InPinId, OutPinId};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 | 
				
			||||||
 | 
					pub enum AnyPin {
 | 
				
			||||||
 | 
					    Out(OutPinId),
 | 
				
			||||||
 | 
					    In(InPinId),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// In the current context, these are the I/O pins of the 'source' node that the newly
 | 
				
			||||||
 | 
					/// created node's I/O pins will connect to.
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub enum AnyPins<'a> {
 | 
				
			||||||
 | 
					    /// Output pins.
 | 
				
			||||||
 | 
					    Out(&'a [OutPinId]),
 | 
				
			||||||
 | 
					    /// Input pins
 | 
				
			||||||
 | 
					    In(&'a [InPinId]),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tiny_fn::tiny_fn! {
 | 
				
			||||||
 | 
					    /// Custom pin shape drawing function with signature
 | 
				
			||||||
 | 
					    /// `Fn(painter: &Painter, rect: Rect, fill: Color32, stroke: Stroke)`
 | 
				
			||||||
 | 
					    pub struct CustomPinShape = Fn(painter: &Painter, rect: Rect, fill: Color32, stroke: Stroke);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Shape of a pin.
 | 
				
			||||||
 | 
					pub enum PinShape {
 | 
				
			||||||
 | 
					    /// Circle shape.
 | 
				
			||||||
 | 
					    Circle,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Triangle shape.
 | 
				
			||||||
 | 
					    Triangle,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Square shape.
 | 
				
			||||||
 | 
					    Square,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Custom shape.
 | 
				
			||||||
 | 
					    Custom(CustomPinShape<'static>),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Information about a pin returned by `SnarlViewer::show_input` and `SnarlViewer::show_output`.
 | 
				
			||||||
 | 
					pub struct PinInfo {
 | 
				
			||||||
 | 
					    /// Shape of the pin.
 | 
				
			||||||
 | 
					    pub shape: PinShape,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Size of the pin.
 | 
				
			||||||
 | 
					    pub size: f32,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Fill color of the pin.
 | 
				
			||||||
 | 
					    pub fill: Color32,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Outline stroke of the pin.
 | 
				
			||||||
 | 
					    pub stroke: Stroke,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for PinInfo {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        PinInfo {
 | 
				
			||||||
 | 
					            shape: PinShape::Circle,
 | 
				
			||||||
 | 
					            size: 1.0,
 | 
				
			||||||
 | 
					            fill: Color32::GRAY,
 | 
				
			||||||
 | 
					            stroke: Stroke::new(1.0, Color32::BLACK),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl PinInfo {
 | 
				
			||||||
 | 
					    /// Sets the shape of the pin.
 | 
				
			||||||
 | 
					    pub fn with_shape(mut self, shape: PinShape) -> Self {
 | 
				
			||||||
 | 
					        self.shape = shape;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Sets the size of the pin.
 | 
				
			||||||
 | 
					    pub fn with_size(mut self, size: f32) -> Self {
 | 
				
			||||||
 | 
					        self.size = size;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Sets the fill color of the pin.
 | 
				
			||||||
 | 
					    pub fn with_fill(mut self, fill: Color32) -> Self {
 | 
				
			||||||
 | 
					        self.fill = fill;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Sets the outline stroke of the pin.
 | 
				
			||||||
 | 
					    pub fn with_stroke(mut self, stroke: Stroke) -> Self {
 | 
				
			||||||
 | 
					        self.stroke = stroke;
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Creates a circle pin.
 | 
				
			||||||
 | 
					    pub fn circle() -> Self {
 | 
				
			||||||
 | 
					        PinInfo {
 | 
				
			||||||
 | 
					            shape: PinShape::Circle,
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Creates a triangle pin.
 | 
				
			||||||
 | 
					    pub fn triangle() -> Self {
 | 
				
			||||||
 | 
					        PinInfo {
 | 
				
			||||||
 | 
					            shape: PinShape::Triangle,
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Creates a square pin.
 | 
				
			||||||
 | 
					    pub fn square() -> Self {
 | 
				
			||||||
 | 
					        PinInfo {
 | 
				
			||||||
 | 
					            shape: PinShape::Square,
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Creates a square pin.
 | 
				
			||||||
 | 
					    pub fn custom<F>(f: F) -> Self
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        F: Fn(&Painter, Rect, Color32, Stroke) + 'static,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        PinInfo {
 | 
				
			||||||
 | 
					            shape: PinShape::Custom(CustomPinShape::new(f)),
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn draw_pin(painter: &Painter, pin: PinInfo, pos: Pos2, base_size: f32) {
 | 
				
			||||||
 | 
					    let size = base_size * pin.size;
 | 
				
			||||||
 | 
					    match pin.shape {
 | 
				
			||||||
 | 
					        PinShape::Circle => {
 | 
				
			||||||
 | 
					            painter.circle(pos, size * 2.0 / std::f32::consts::PI, pin.fill, pin.stroke);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        PinShape::Triangle => {
 | 
				
			||||||
 | 
					            const A: Vec2 = vec2(-0.649_519, 0.4875);
 | 
				
			||||||
 | 
					            const B: Vec2 = vec2(0.649_519, 0.4875);
 | 
				
			||||||
 | 
					            const C: Vec2 = vec2(0.0, -0.6375);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let points = vec![pos + A * size, pos + B * size, pos + C * size];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            painter.add(Shape::Path(PathShape {
 | 
				
			||||||
 | 
					                points,
 | 
				
			||||||
 | 
					                closed: true,
 | 
				
			||||||
 | 
					                fill: pin.fill,
 | 
				
			||||||
 | 
					                stroke: pin.stroke,
 | 
				
			||||||
 | 
					            }));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        PinShape::Square => {
 | 
				
			||||||
 | 
					            let points = vec![
 | 
				
			||||||
 | 
					                pos + vec2(-0.5, -0.5) * size,
 | 
				
			||||||
 | 
					                pos + vec2(0.5, -0.5) * size,
 | 
				
			||||||
 | 
					                pos + vec2(0.5, 0.5) * size,
 | 
				
			||||||
 | 
					                pos + vec2(-0.5, 0.5) * size,
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            painter.add(Shape::Path(PathShape {
 | 
				
			||||||
 | 
					                points,
 | 
				
			||||||
 | 
					                closed: true,
 | 
				
			||||||
 | 
					                fill: pin.fill,
 | 
				
			||||||
 | 
					                stroke: pin.stroke,
 | 
				
			||||||
 | 
					            }));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        PinShape::Custom(f) => f.call(
 | 
				
			||||||
 | 
					            painter,
 | 
				
			||||||
 | 
					            Rect::from_center_size(pos, vec2(size, size)),
 | 
				
			||||||
 | 
					            pin.fill,
 | 
				
			||||||
 | 
					            pin.stroke,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										404
									
								
								vendor/egui-snarl/src/ui/state.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										404
									
								
								vendor/egui-snarl/src/ui/state.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,404 @@
 | 
				
			||||||
 | 
					use egui::{style::Spacing, Align, Context, Id, Pos2, Rect, Vec2};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{InPinId, OutPinId, Snarl};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::SnarlStyle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Node UI state.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct NodeState {
 | 
				
			||||||
 | 
					    /// Node size for this frame.
 | 
				
			||||||
 | 
					    /// It is updated to fit content.
 | 
				
			||||||
 | 
					    size: Vec2,
 | 
				
			||||||
 | 
					    header_height: f32,
 | 
				
			||||||
 | 
					    body_width: f32,
 | 
				
			||||||
 | 
					    footer_width: f32,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    id: Id,
 | 
				
			||||||
 | 
					    scale: f32,
 | 
				
			||||||
 | 
					    dirty: bool,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, PartialEq)]
 | 
				
			||||||
 | 
					struct NodeData {
 | 
				
			||||||
 | 
					    unscaled_size: Vec2,
 | 
				
			||||||
 | 
					    unscaled_header_height: f32,
 | 
				
			||||||
 | 
					    unscaled_body_width: f32,
 | 
				
			||||||
 | 
					    unsacled_footer_width: f32,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl NodeState {
 | 
				
			||||||
 | 
					    pub fn load(cx: &Context, id: Id, spacing: &Spacing, scale: f32) -> Self {
 | 
				
			||||||
 | 
					        match cx.data_mut(|d| d.get_temp::<NodeData>(id)) {
 | 
				
			||||||
 | 
					            Some(data) => NodeState {
 | 
				
			||||||
 | 
					                size: data.unscaled_size * scale,
 | 
				
			||||||
 | 
					                header_height: data.unscaled_header_height * scale,
 | 
				
			||||||
 | 
					                body_width: data.unscaled_body_width * scale,
 | 
				
			||||||
 | 
					                footer_width: data.unsacled_footer_width * scale,
 | 
				
			||||||
 | 
					                id,
 | 
				
			||||||
 | 
					                scale,
 | 
				
			||||||
 | 
					                dirty: false,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            None => Self::initial(id, spacing, scale),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn clear(self, cx: &Context) {
 | 
				
			||||||
 | 
					        cx.data_mut(|d| d.remove::<Self>(self.id));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn store(&self, cx: &Context) {
 | 
				
			||||||
 | 
					        if self.dirty {
 | 
				
			||||||
 | 
					            cx.data_mut(|d| {
 | 
				
			||||||
 | 
					                d.insert_temp(
 | 
				
			||||||
 | 
					                    self.id,
 | 
				
			||||||
 | 
					                    NodeData {
 | 
				
			||||||
 | 
					                        unscaled_size: self.size / self.scale,
 | 
				
			||||||
 | 
					                        unscaled_header_height: self.header_height / self.scale,
 | 
				
			||||||
 | 
					                        unscaled_body_width: self.body_width / self.scale,
 | 
				
			||||||
 | 
					                        unsacled_footer_width: self.footer_width / self.scale,
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Finds node rect at specific position (excluding node frame margin).
 | 
				
			||||||
 | 
					    pub fn node_rect(&self, pos: Pos2, openness: f32) -> Rect {
 | 
				
			||||||
 | 
					        Rect::from_min_size(
 | 
				
			||||||
 | 
					            pos,
 | 
				
			||||||
 | 
					            egui::vec2(
 | 
				
			||||||
 | 
					                self.size.x,
 | 
				
			||||||
 | 
					                f32::max(self.header_height, self.size.y * openness),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn payload_offset(&self, openness: f32) -> f32 {
 | 
				
			||||||
 | 
					        (self.size.y) * (1.0 - openness)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn align_body(&mut self, rect: Rect) -> Rect {
 | 
				
			||||||
 | 
					        let x_range = Align::Center.align_size_within_range(self.body_width, rect.x_range());
 | 
				
			||||||
 | 
					        Rect::from_x_y_ranges(x_range, rect.y_range())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn align_footer(&mut self, rect: Rect) -> Rect {
 | 
				
			||||||
 | 
					        let x_range = Align::Center.align_size_within_range(self.footer_width, rect.x_range());
 | 
				
			||||||
 | 
					        Rect::from_x_y_ranges(x_range, rect.y_range())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_size(&mut self, size: Vec2) {
 | 
				
			||||||
 | 
					        if self.size != size {
 | 
				
			||||||
 | 
					            self.size = size;
 | 
				
			||||||
 | 
					            self.dirty = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_header_height(&mut self, height: f32) {
 | 
				
			||||||
 | 
					        if self.header_height != height {
 | 
				
			||||||
 | 
					            self.header_height = height;
 | 
				
			||||||
 | 
					            self.dirty = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_body_width(&mut self, width: f32) {
 | 
				
			||||||
 | 
					        if self.body_width != width {
 | 
				
			||||||
 | 
					            self.body_width = width;
 | 
				
			||||||
 | 
					            self.dirty = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_footer_width(&mut self, width: f32) {
 | 
				
			||||||
 | 
					        if self.footer_width != width {
 | 
				
			||||||
 | 
					            self.footer_width = width;
 | 
				
			||||||
 | 
					            self.dirty = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn initial(id: Id, spacing: &Spacing, scale: f32) -> Self {
 | 
				
			||||||
 | 
					        NodeState {
 | 
				
			||||||
 | 
					            size: spacing.interact_size,
 | 
				
			||||||
 | 
					            header_height: spacing.interact_size.y,
 | 
				
			||||||
 | 
					            body_width: 0.0,
 | 
				
			||||||
 | 
					            footer_width: 0.0,
 | 
				
			||||||
 | 
					            id,
 | 
				
			||||||
 | 
					            dirty: true,
 | 
				
			||||||
 | 
					            scale,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone)]
 | 
				
			||||||
 | 
					pub enum NewWires {
 | 
				
			||||||
 | 
					    In(Vec<InPinId>),
 | 
				
			||||||
 | 
					    Out(Vec<OutPinId>),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct SnarlState {
 | 
				
			||||||
 | 
					    /// Where viewport's center in graph's space.
 | 
				
			||||||
 | 
					    offset: Vec2,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Scale of the viewport.
 | 
				
			||||||
 | 
					    scale: f32,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    target_scale: f32,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    new_wires: Option<NewWires>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    id: Id,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Flag indicating that the graph state is dirty must be saved.
 | 
				
			||||||
 | 
					    dirty: bool,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Flag indicating that the link menu is open.
 | 
				
			||||||
 | 
					    is_link_menu_open: bool,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone)]
 | 
				
			||||||
 | 
					struct SnarlStateData {
 | 
				
			||||||
 | 
					    offset: Vec2,
 | 
				
			||||||
 | 
					    scale: f32,
 | 
				
			||||||
 | 
					    target_scale: f32,
 | 
				
			||||||
 | 
					    new_wires: Option<NewWires>,
 | 
				
			||||||
 | 
					    is_link_menu_open: bool,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SnarlState {
 | 
				
			||||||
 | 
					    pub fn load<T>(
 | 
				
			||||||
 | 
					        cx: &Context,
 | 
				
			||||||
 | 
					        id: Id,
 | 
				
			||||||
 | 
					        pivot: Pos2,
 | 
				
			||||||
 | 
					        viewport: Rect,
 | 
				
			||||||
 | 
					        snarl: &Snarl<T>,
 | 
				
			||||||
 | 
					        style: &SnarlStyle,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        let Some(mut data) = cx.data_mut(|d| d.get_temp::<SnarlStateData>(id)) else {
 | 
				
			||||||
 | 
					            return Self::initial(id, viewport, snarl, style);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let new_scale = cx.animate_value_with_time(id.with("zoom-scale"), data.target_scale, 0.1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut dirty = false;
 | 
				
			||||||
 | 
					        if new_scale != data.scale {
 | 
				
			||||||
 | 
					            let a = pivot + data.offset - viewport.center().to_vec2();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            data.offset += a * new_scale / data.scale - a;
 | 
				
			||||||
 | 
					            data.scale = new_scale;
 | 
				
			||||||
 | 
					            dirty = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SnarlState {
 | 
				
			||||||
 | 
					            offset: data.offset,
 | 
				
			||||||
 | 
					            scale: data.scale,
 | 
				
			||||||
 | 
					            target_scale: data.target_scale,
 | 
				
			||||||
 | 
					            new_wires: data.new_wires,
 | 
				
			||||||
 | 
					            is_link_menu_open: data.is_link_menu_open,
 | 
				
			||||||
 | 
					            id,
 | 
				
			||||||
 | 
					            dirty,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn initial<T>(id: Id, viewport: Rect, snarl: &Snarl<T>, style: &SnarlStyle) -> Self {
 | 
				
			||||||
 | 
					        let mut bb = Rect::NOTHING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (_, node) in snarl.nodes.iter() {
 | 
				
			||||||
 | 
					            bb.extend_with(node.pos);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if !bb.is_positive() {
 | 
				
			||||||
 | 
					            let scale = 1.0f32.clamp(style.min_scale, style.max_scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return SnarlState {
 | 
				
			||||||
 | 
					                offset: Vec2::ZERO,
 | 
				
			||||||
 | 
					                scale,
 | 
				
			||||||
 | 
					                target_scale: scale,
 | 
				
			||||||
 | 
					                new_wires: None,
 | 
				
			||||||
 | 
					                is_link_menu_open: false,
 | 
				
			||||||
 | 
					                id,
 | 
				
			||||||
 | 
					                dirty: true,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bb = bb.expand(100.0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let bb_size = bb.size();
 | 
				
			||||||
 | 
					        let viewport_size = viewport.size();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let scale = (viewport_size.x / bb_size.x)
 | 
				
			||||||
 | 
					            .min(1.0)
 | 
				
			||||||
 | 
					            .min(viewport_size.y / bb_size.y)
 | 
				
			||||||
 | 
					            .min(style.max_scale)
 | 
				
			||||||
 | 
					            .max(style.min_scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let offset = bb.center().to_vec2() * scale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SnarlState {
 | 
				
			||||||
 | 
					            offset,
 | 
				
			||||||
 | 
					            scale,
 | 
				
			||||||
 | 
					            target_scale: scale,
 | 
				
			||||||
 | 
					            new_wires: None,
 | 
				
			||||||
 | 
					            is_link_menu_open: false,
 | 
				
			||||||
 | 
					            id,
 | 
				
			||||||
 | 
					            dirty: true,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn store(self, cx: &Context) {
 | 
				
			||||||
 | 
					        if self.dirty {
 | 
				
			||||||
 | 
					            cx.data_mut(|d| {
 | 
				
			||||||
 | 
					                d.insert_temp(
 | 
				
			||||||
 | 
					                    self.id,
 | 
				
			||||||
 | 
					                    SnarlStateData {
 | 
				
			||||||
 | 
					                        offset: self.offset,
 | 
				
			||||||
 | 
					                        scale: self.scale,
 | 
				
			||||||
 | 
					                        target_scale: self.target_scale,
 | 
				
			||||||
 | 
					                        new_wires: self.new_wires,
 | 
				
			||||||
 | 
					                        is_link_menu_open: self.is_link_menu_open,
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn pan(&mut self, delta: Vec2) {
 | 
				
			||||||
 | 
					        self.offset += delta;
 | 
				
			||||||
 | 
					        self.dirty = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn scale(&self) -> f32 {
 | 
				
			||||||
 | 
					        self.scale
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn offset(&self) -> Vec2 {
 | 
				
			||||||
 | 
					        self.offset
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn set_scale(&mut self, scale: f32) {
 | 
				
			||||||
 | 
					        self.target_scale = scale;
 | 
				
			||||||
 | 
					        self.dirty = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn screen_pos_to_graph(&self, pos: Pos2, viewport: Rect) -> Pos2 {
 | 
				
			||||||
 | 
					        (pos + self.offset - viewport.center().to_vec2()) / self.scale
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn graph_pos_to_screen(&self, pos: Pos2, viewport: Rect) -> Pos2 {
 | 
				
			||||||
 | 
					        pos * self.scale - self.offset + viewport.center().to_vec2()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // #[inline(always)]
 | 
				
			||||||
 | 
					    // pub fn graph_vec_to_screen(&self, size: Vec2) -> Vec2 {
 | 
				
			||||||
 | 
					    //     size * self.scale
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    pub fn screen_vec_to_graph(&self, size: Vec2) -> Vec2 {
 | 
				
			||||||
 | 
					        size / self.scale
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // #[inline(always)]
 | 
				
			||||||
 | 
					    // pub fn graph_value_to_screen(&self, value: f32) -> f32 {
 | 
				
			||||||
 | 
					    //     value * self.scale
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // #[inline(always)]
 | 
				
			||||||
 | 
					    // pub fn screen_value_to_graph(&self, value: f32) -> f32 {
 | 
				
			||||||
 | 
					    //     value / self.scale
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn start_new_wire_in(&mut self, pin: InPinId) {
 | 
				
			||||||
 | 
					        self.new_wires = Some(NewWires::In(vec![pin]));
 | 
				
			||||||
 | 
					        self.dirty = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn start_new_wire_out(&mut self, pin: OutPinId) {
 | 
				
			||||||
 | 
					        self.new_wires = Some(NewWires::Out(vec![pin]));
 | 
				
			||||||
 | 
					        self.dirty = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn start_new_wires_in(&mut self, pins: &[InPinId]) {
 | 
				
			||||||
 | 
					        self.new_wires = Some(NewWires::In(pins.to_vec()));
 | 
				
			||||||
 | 
					        self.dirty = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn start_new_wires_out(&mut self, pins: &[OutPinId]) {
 | 
				
			||||||
 | 
					        self.new_wires = Some(NewWires::Out(pins.to_vec()));
 | 
				
			||||||
 | 
					        self.dirty = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn add_new_wire_in(&mut self, pin: InPinId) {
 | 
				
			||||||
 | 
					        if let Some(NewWires::In(pins)) = &mut self.new_wires {
 | 
				
			||||||
 | 
					            if !pins.contains(&pin) {
 | 
				
			||||||
 | 
					                pins.push(pin);
 | 
				
			||||||
 | 
					                self.dirty = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn add_new_wire_out(&mut self, pin: OutPinId) {
 | 
				
			||||||
 | 
					        if let Some(NewWires::Out(pins)) = &mut self.new_wires {
 | 
				
			||||||
 | 
					            if !pins.contains(&pin) {
 | 
				
			||||||
 | 
					                pins.push(pin);
 | 
				
			||||||
 | 
					                self.dirty = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn remove_new_wire_in(&mut self, pin: InPinId) {
 | 
				
			||||||
 | 
					        if let Some(NewWires::In(pins)) = &mut self.new_wires {
 | 
				
			||||||
 | 
					            if let Some(idx) = pins.iter().position(|p| *p == pin) {
 | 
				
			||||||
 | 
					                pins.swap_remove(idx);
 | 
				
			||||||
 | 
					                self.dirty = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn remove_new_wire_out(&mut self, pin: OutPinId) {
 | 
				
			||||||
 | 
					        if let Some(NewWires::Out(pins)) = &mut self.new_wires {
 | 
				
			||||||
 | 
					            if let Some(idx) = pins.iter().position(|p| *p == pin) {
 | 
				
			||||||
 | 
					                pins.swap_remove(idx);
 | 
				
			||||||
 | 
					                self.dirty = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn has_new_wires(&self) -> bool {
 | 
				
			||||||
 | 
					        self.new_wires.is_some()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn new_wires(&self) -> Option<&NewWires> {
 | 
				
			||||||
 | 
					        self.new_wires.as_ref()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn take_wires(&mut self) -> Option<NewWires> {
 | 
				
			||||||
 | 
					        self.dirty |= self.new_wires.is_some();
 | 
				
			||||||
 | 
					        self.new_wires.take()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub(crate) fn revert_take_wires(&mut self, wires: NewWires) {
 | 
				
			||||||
 | 
					        self.new_wires = Some(wires);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub(crate) fn open_link_menu(&mut self) {
 | 
				
			||||||
 | 
					        self.is_link_menu_open = true;
 | 
				
			||||||
 | 
					        self.dirty = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub(crate) fn close_link_menu(&mut self) {
 | 
				
			||||||
 | 
					        self.new_wires = None;
 | 
				
			||||||
 | 
					        self.is_link_menu_open = false;
 | 
				
			||||||
 | 
					        self.dirty = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub(crate) fn is_link_menu_open(&self) -> bool {
 | 
				
			||||||
 | 
					        self.is_link_menu_open
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										186
									
								
								vendor/egui-snarl/src/ui/viewer.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								vendor/egui-snarl/src/ui/viewer.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,186 @@
 | 
				
			||||||
 | 
					use egui::{Color32, Pos2, Style, Ui};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{InPin, NodeId, OutPin, Snarl};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::pin::{AnyPins, PinInfo};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// SnarlViewer is a trait for viewing a Snarl.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// It can extract necessary data from the nodes and controls their
 | 
				
			||||||
 | 
					/// response to certain events.
 | 
				
			||||||
 | 
					pub trait SnarlViewer<T> {
 | 
				
			||||||
 | 
					    /// Returns title of the node.
 | 
				
			||||||
 | 
					    fn title(&mut self, node: &T) -> String;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Checks if node has something to show in body - between input and output pins.
 | 
				
			||||||
 | 
					    fn has_body(&mut self, node: &T) -> bool {
 | 
				
			||||||
 | 
					        let _ = node;
 | 
				
			||||||
 | 
					        false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Checks if node has something to show in footer - below pins and body.
 | 
				
			||||||
 | 
					    fn has_footer(&mut self, node: &T) -> bool {
 | 
				
			||||||
 | 
					        let _ = node;
 | 
				
			||||||
 | 
					        false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Checks if node has something to show in on-hover popup.
 | 
				
			||||||
 | 
					    fn has_on_hover_popup(&mut self, node: &T) -> bool {
 | 
				
			||||||
 | 
					        let _ = node;
 | 
				
			||||||
 | 
					        false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Renders the node's header.
 | 
				
			||||||
 | 
					    fn show_header(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        node: NodeId,
 | 
				
			||||||
 | 
					        inputs: &[InPin],
 | 
				
			||||||
 | 
					        outputs: &[OutPin],
 | 
				
			||||||
 | 
					        ui: &mut Ui,
 | 
				
			||||||
 | 
					        scale: f32,
 | 
				
			||||||
 | 
					        snarl: &mut Snarl<T>,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        let _ = (inputs, outputs, scale);
 | 
				
			||||||
 | 
					        ui.label(self.title(&snarl[node]));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns number of output pins of the node.
 | 
				
			||||||
 | 
					    fn outputs(&mut self, node: &T) -> usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns number of input pins of the node.
 | 
				
			||||||
 | 
					    fn inputs(&mut self, node: &T) -> usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Renders the node's input pin.
 | 
				
			||||||
 | 
					    fn show_input(&mut self, pin: &InPin, ui: &mut Ui, scale: f32, snarl: &mut Snarl<T>)
 | 
				
			||||||
 | 
					        -> PinInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Renders the node's output pin.
 | 
				
			||||||
 | 
					    fn show_output(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        pin: &OutPin,
 | 
				
			||||||
 | 
					        ui: &mut Ui,
 | 
				
			||||||
 | 
					        scale: f32,
 | 
				
			||||||
 | 
					        snarl: &mut Snarl<T>,
 | 
				
			||||||
 | 
					    ) -> PinInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Renders the node's body.
 | 
				
			||||||
 | 
					    fn show_body(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        node: NodeId,
 | 
				
			||||||
 | 
					        inputs: &[InPin],
 | 
				
			||||||
 | 
					        outputs: &[OutPin],
 | 
				
			||||||
 | 
					        ui: &mut Ui,
 | 
				
			||||||
 | 
					        scale: f32,
 | 
				
			||||||
 | 
					        snarl: &mut Snarl<T>,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        let _ = (node, inputs, outputs, ui, scale, snarl);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Renders the node's footer.
 | 
				
			||||||
 | 
					    fn show_footer(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        node: NodeId,
 | 
				
			||||||
 | 
					        inputs: &[InPin],
 | 
				
			||||||
 | 
					        outputs: &[OutPin],
 | 
				
			||||||
 | 
					        ui: &mut Ui,
 | 
				
			||||||
 | 
					        scale: f32,
 | 
				
			||||||
 | 
					        snarl: &mut Snarl<T>,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        let _ = (node, inputs, outputs, ui, scale, snarl);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Renders the node's on-hover popup.
 | 
				
			||||||
 | 
					    fn show_on_hover_popup(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        node: NodeId,
 | 
				
			||||||
 | 
					        inputs: &[InPin],
 | 
				
			||||||
 | 
					        outputs: &[OutPin],
 | 
				
			||||||
 | 
					        ui: &mut Ui,
 | 
				
			||||||
 | 
					        scale: f32,
 | 
				
			||||||
 | 
					        snarl: &mut Snarl<T>,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        let _ = (node, inputs, outputs, ui, scale, snarl);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns color of the node's input pin.
 | 
				
			||||||
 | 
					    /// Called when pin in not visible.
 | 
				
			||||||
 | 
					    fn input_color(&mut self, pin: &InPin, style: &Style, snarl: &mut Snarl<T>) -> Color32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns color of the node's output pin.
 | 
				
			||||||
 | 
					    /// Called when pin in not visible.
 | 
				
			||||||
 | 
					    fn output_color(&mut self, pin: &OutPin, style: &Style, snarl: &mut Snarl<T>) -> Color32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Show context menu for the snarl.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This can be used to implement menu for adding new nodes.
 | 
				
			||||||
 | 
					    fn graph_menu(&mut self, pos: Pos2, ui: &mut Ui, scale: f32, snarl: &mut Snarl<T>) {
 | 
				
			||||||
 | 
					        let _ = (pos, ui, scale, snarl);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Show context menu for the snarl. This menu is opened when releasing a pin to empty
 | 
				
			||||||
 | 
					    /// space. It can be used to implement menu for adding new node, and directly
 | 
				
			||||||
 | 
					    /// connecting it to the released wire.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    fn graph_menu_for_dropped_wire(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        pos: Pos2,
 | 
				
			||||||
 | 
					        ui: &mut Ui,
 | 
				
			||||||
 | 
					        scale: f32,
 | 
				
			||||||
 | 
					        src_pins: AnyPins,
 | 
				
			||||||
 | 
					        snarl: &mut Snarl<T>,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        let _ = (pos, scale, src_pins, snarl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Default implementation simply doesn't utilize wire drop.
 | 
				
			||||||
 | 
					        ui.close_menu();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Show context menu for the snarl.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This can be used to implement menu for adding new nodes.
 | 
				
			||||||
 | 
					    fn node_menu(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        node: NodeId,
 | 
				
			||||||
 | 
					        inputs: &[InPin],
 | 
				
			||||||
 | 
					        outputs: &[OutPin],
 | 
				
			||||||
 | 
					        ui: &mut Ui,
 | 
				
			||||||
 | 
					        scale: f32,
 | 
				
			||||||
 | 
					        snarl: &mut Snarl<T>,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        let _ = (node, inputs, outputs, ui, scale, snarl);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Asks the viewer to connect two pins.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This is usually happens when user drags a wire from one node's output pin to another node's input pin or vice versa.
 | 
				
			||||||
 | 
					    /// By default this method connects the pins and returns `Ok(())`.
 | 
				
			||||||
 | 
					    #[inline]
 | 
				
			||||||
 | 
					    fn connect(&mut self, from: &OutPin, to: &InPin, snarl: &mut Snarl<T>) {
 | 
				
			||||||
 | 
					        snarl.connect(from.id, to.id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Asks the viewer to disconnect two pins.
 | 
				
			||||||
 | 
					    #[inline]
 | 
				
			||||||
 | 
					    fn disconnect(&mut self, from: &OutPin, to: &InPin, snarl: &mut Snarl<T>) {
 | 
				
			||||||
 | 
					        snarl.disconnect(from.id, to.id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Asks the viewer to disconnect all wires from the output pin.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This is usually happens when right-clicking on an output pin.
 | 
				
			||||||
 | 
					    /// By default this method disconnects the pins and returns `Ok(())`.
 | 
				
			||||||
 | 
					    #[inline]
 | 
				
			||||||
 | 
					    fn drop_outputs(&mut self, pin: &OutPin, snarl: &mut Snarl<T>) {
 | 
				
			||||||
 | 
					        snarl.drop_outputs(pin.id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Asks the viewer to disconnect all wires from the input pin.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This is usually happens when right-clicking on an input pin.
 | 
				
			||||||
 | 
					    /// By default this method disconnects the pins and returns `Ok(())`.
 | 
				
			||||||
 | 
					    #[inline]
 | 
				
			||||||
 | 
					    fn drop_inputs(&mut self, pin: &InPin, snarl: &mut Snarl<T>) {
 | 
				
			||||||
 | 
					        snarl.drop_inputs(pin.id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										323
									
								
								vendor/egui-snarl/src/ui/wire.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								vendor/egui-snarl/src/ui/wire.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,323 @@
 | 
				
			||||||
 | 
					use egui::{epaint::PathShape, pos2, Color32, Pos2, Rect, Shape, Stroke, Ui};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Layer where wires are rendered.
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "egui-probe", derive(egui_probe::EguiProbe))]
 | 
				
			||||||
 | 
					pub enum WireLayer {
 | 
				
			||||||
 | 
					    /// Wires are rendered behind nodes.
 | 
				
			||||||
 | 
					    /// This is default.
 | 
				
			||||||
 | 
					    BehindNodes,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Wires are rendered above nodes.
 | 
				
			||||||
 | 
					    AboveNodes,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Returns 6th degree bezier curve for the wire
 | 
				
			||||||
 | 
					fn wire_bezier(
 | 
				
			||||||
 | 
					    mut frame_size: f32,
 | 
				
			||||||
 | 
					    upscale: bool,
 | 
				
			||||||
 | 
					    downscale: bool,
 | 
				
			||||||
 | 
					    from: Pos2,
 | 
				
			||||||
 | 
					    to: Pos2,
 | 
				
			||||||
 | 
					) -> [Pos2; 6] {
 | 
				
			||||||
 | 
					    if upscale {
 | 
				
			||||||
 | 
					        frame_size = frame_size.max((from - to).length() / 4.0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if downscale {
 | 
				
			||||||
 | 
					        frame_size = frame_size.min((from - to).length() / 4.0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let from_norm_x = frame_size;
 | 
				
			||||||
 | 
					    let from_2 = pos2(from.x + from_norm_x, from.y);
 | 
				
			||||||
 | 
					    let to_norm_x = -from_norm_x;
 | 
				
			||||||
 | 
					    let to_2 = pos2(to.x + to_norm_x, to.y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let between = (from_2 - to_2).length();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if from_2.x <= to_2.x && between >= frame_size * 2.0 {
 | 
				
			||||||
 | 
					        let middle_1 = from_2 + (to_2 - from_2).normalized() * frame_size;
 | 
				
			||||||
 | 
					        let middle_2 = to_2 + (from_2 - to_2).normalized() * frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [from, from_2, middle_1, middle_2, to_2, to]
 | 
				
			||||||
 | 
					    } else if from_2.x <= to_2.x {
 | 
				
			||||||
 | 
					        let t =
 | 
				
			||||||
 | 
					            (between - (to_2.y - from_2.y).abs()) / (frame_size * 2.0 - (to_2.y - from_2.y).abs());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut middle_1 = from_2 + (to_2 - from_2).normalized() * frame_size;
 | 
				
			||||||
 | 
					        let mut middle_2 = to_2 + (from_2 - to_2).normalized() * frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if from_2.y >= to_2.y + frame_size {
 | 
				
			||||||
 | 
					            let u = (from_2.y - to_2.y - frame_size) / frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let t0_middle_1 = pos2(from_2.x + (1.0 - u) * frame_size, from_2.y - frame_size * u);
 | 
				
			||||||
 | 
					            let t0_middle_2 = pos2(to_2.x, to_2.y + frame_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            middle_1 = t0_middle_1.lerp(middle_1, t);
 | 
				
			||||||
 | 
					            middle_2 = t0_middle_2.lerp(middle_2, t);
 | 
				
			||||||
 | 
					        } else if from_2.y >= to_2.y {
 | 
				
			||||||
 | 
					            let u = (from_2.y - to_2.y) / frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let t0_middle_1 = pos2(from_2.x + u * frame_size, from_2.y + frame_size * (1.0 - u));
 | 
				
			||||||
 | 
					            let t0_middle_2 = pos2(to_2.x, to_2.y + frame_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            middle_1 = t0_middle_1.lerp(middle_1, t);
 | 
				
			||||||
 | 
					            middle_2 = t0_middle_2.lerp(middle_2, t);
 | 
				
			||||||
 | 
					        } else if to_2.y >= from_2.y + frame_size {
 | 
				
			||||||
 | 
					            let u = (to_2.y - from_2.y - frame_size) / frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let t0_middle_1 = pos2(from_2.x, from_2.y + frame_size);
 | 
				
			||||||
 | 
					            let t0_middle_2 = pos2(to_2.x - (1.0 - u) * frame_size, to_2.y - frame_size * u);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            middle_1 = t0_middle_1.lerp(middle_1, t);
 | 
				
			||||||
 | 
					            middle_2 = t0_middle_2.lerp(middle_2, t);
 | 
				
			||||||
 | 
					        } else if to_2.y >= from_2.y {
 | 
				
			||||||
 | 
					            let u = (to_2.y - from_2.y) / frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let t0_middle_1 = pos2(from_2.x, from_2.y + frame_size);
 | 
				
			||||||
 | 
					            let t0_middle_2 = pos2(to_2.x - u * frame_size, to_2.y + frame_size * (1.0 - u));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            middle_1 = t0_middle_1.lerp(middle_1, t);
 | 
				
			||||||
 | 
					            middle_2 = t0_middle_2.lerp(middle_2, t);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            unreachable!();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [from, from_2, middle_1, middle_2, to_2, to]
 | 
				
			||||||
 | 
					    } else if from_2.y >= to_2.y + frame_size * 2.0 {
 | 
				
			||||||
 | 
					        let middle_1 = pos2(from_2.x, from_2.y - frame_size);
 | 
				
			||||||
 | 
					        let middle_2 = pos2(to_2.x, to_2.y + frame_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [from, from_2, middle_1, middle_2, to_2, to]
 | 
				
			||||||
 | 
					    } else if from_2.y >= to_2.y + frame_size {
 | 
				
			||||||
 | 
					        let t = (from_2.y - to_2.y - frame_size) / frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let middle_1 = pos2(from_2.x + (1.0 - t) * frame_size, from_2.y - frame_size * t);
 | 
				
			||||||
 | 
					        let middle_2 = pos2(to_2.x, to_2.y + frame_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [from, from_2, middle_1, middle_2, to_2, to]
 | 
				
			||||||
 | 
					    } else if from_2.y >= to_2.y {
 | 
				
			||||||
 | 
					        let t = (from_2.y - to_2.y) / frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let middle_1 = pos2(from_2.x + t * frame_size, from_2.y + frame_size * (1.0 - t));
 | 
				
			||||||
 | 
					        let middle_2 = pos2(to_2.x, to_2.y + frame_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [from, from_2, middle_1, middle_2, to_2, to]
 | 
				
			||||||
 | 
					    } else if to_2.y >= from_2.y + frame_size * 2.0 {
 | 
				
			||||||
 | 
					        let middle_1 = pos2(from_2.x, from_2.y + frame_size);
 | 
				
			||||||
 | 
					        let middle_2 = pos2(to_2.x, to_2.y - frame_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [from, from_2, middle_1, middle_2, to_2, to]
 | 
				
			||||||
 | 
					    } else if to_2.y >= from_2.y + frame_size {
 | 
				
			||||||
 | 
					        let t = (to_2.y - from_2.y - frame_size) / frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let middle_1 = pos2(from_2.x, from_2.y + frame_size);
 | 
				
			||||||
 | 
					        let middle_2 = pos2(to_2.x - (1.0 - t) * frame_size, to_2.y - frame_size * t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [from, from_2, middle_1, middle_2, to_2, to]
 | 
				
			||||||
 | 
					    } else if to_2.y >= from_2.y {
 | 
				
			||||||
 | 
					        let t = (to_2.y - from_2.y) / frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let middle_1 = pos2(from_2.x, from_2.y + frame_size);
 | 
				
			||||||
 | 
					        let middle_2 = pos2(to_2.x - t * frame_size, to_2.y + frame_size * (1.0 - t));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [from, from_2, middle_1, middle_2, to_2, to]
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        unreachable!();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[allow(clippy::too_many_arguments)]
 | 
				
			||||||
 | 
					pub fn draw_wire(
 | 
				
			||||||
 | 
					    ui: &mut Ui,
 | 
				
			||||||
 | 
					    shapes: &mut Vec<Shape>,
 | 
				
			||||||
 | 
					    frame_size: f32,
 | 
				
			||||||
 | 
					    upscale: bool,
 | 
				
			||||||
 | 
					    downscale: bool,
 | 
				
			||||||
 | 
					    from: Pos2,
 | 
				
			||||||
 | 
					    to: Pos2,
 | 
				
			||||||
 | 
					    stroke: Stroke,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    let points = wire_bezier(frame_size, upscale, downscale, from, to);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let bb = Rect::from_points(&points);
 | 
				
			||||||
 | 
					    if ui.is_rect_visible(bb) {
 | 
				
			||||||
 | 
					        draw_bezier(shapes, &points, stroke);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn hit_wire(
 | 
				
			||||||
 | 
					    pos: Pos2,
 | 
				
			||||||
 | 
					    frame_size: f32,
 | 
				
			||||||
 | 
					    upscale: bool,
 | 
				
			||||||
 | 
					    downscale: bool,
 | 
				
			||||||
 | 
					    from: Pos2,
 | 
				
			||||||
 | 
					    to: Pos2,
 | 
				
			||||||
 | 
					    threshold: f32,
 | 
				
			||||||
 | 
					) -> bool {
 | 
				
			||||||
 | 
					    let points = wire_bezier(frame_size, upscale, downscale, from, to);
 | 
				
			||||||
 | 
					    hit_bezier(pos, &points, threshold)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn bezier_reference_size(points: &[Pos2; 6]) -> f32 {
 | 
				
			||||||
 | 
					    let [p0, p1, p2, p3, p4, p5] = *points;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (p1 - p0).length()
 | 
				
			||||||
 | 
					        + (p2 - p1).length()
 | 
				
			||||||
 | 
					        + (p3 - p2).length()
 | 
				
			||||||
 | 
					        + (p4 - p3).length()
 | 
				
			||||||
 | 
					        + (p5 - p4).length()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const MAX_BEZIER_SAMPLES: usize = 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn bezier_samples_number(points: &[Pos2; 6], threshold: f32) -> usize {
 | 
				
			||||||
 | 
					    let reference_size = bezier_reference_size(points);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[allow(clippy::cast_sign_loss)]
 | 
				
			||||||
 | 
					    #[allow(clippy::cast_possible_truncation)]
 | 
				
			||||||
 | 
					    ((reference_size / threshold).ceil().max(0.0) as usize).min(MAX_BEZIER_SAMPLES)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn draw_bezier(shapes: &mut Vec<Shape>, points: &[Pos2; 6], mut stroke: Stroke) {
 | 
				
			||||||
 | 
					    if stroke.width < 1.0 {
 | 
				
			||||||
 | 
					        stroke.color = stroke.color.gamma_multiply(stroke.width);
 | 
				
			||||||
 | 
					        stroke.width = 1.0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let samples = bezier_samples_number(points, stroke.width);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut path = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for i in 0..samples {
 | 
				
			||||||
 | 
					        #[allow(clippy::cast_precision_loss)]
 | 
				
			||||||
 | 
					        let t = i as f32 / (samples - 1) as f32;
 | 
				
			||||||
 | 
					        path.push(sample_bezier(points, t));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let shape = Shape::Path(PathShape {
 | 
				
			||||||
 | 
					        points: path,
 | 
				
			||||||
 | 
					        closed: false,
 | 
				
			||||||
 | 
					        fill: Color32::TRANSPARENT,
 | 
				
			||||||
 | 
					        stroke,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    shapes.push(shape);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[allow(clippy::let_and_return)]
 | 
				
			||||||
 | 
					fn sample_bezier(points: &[Pos2; 6], t: f32) -> Pos2 {
 | 
				
			||||||
 | 
					    let [p0, p1, p2, p3, p4, p5] = *points;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let p0_0 = p0;
 | 
				
			||||||
 | 
					    let p1_0 = p1;
 | 
				
			||||||
 | 
					    let p2_0 = p2;
 | 
				
			||||||
 | 
					    let p3_0 = p3;
 | 
				
			||||||
 | 
					    let p4_0 = p4;
 | 
				
			||||||
 | 
					    let p5_0 = p5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let p0_1 = p0_0.lerp(p1_0, t);
 | 
				
			||||||
 | 
					    let p1_1 = p1_0.lerp(p2_0, t);
 | 
				
			||||||
 | 
					    let p2_1 = p2_0.lerp(p3_0, t);
 | 
				
			||||||
 | 
					    let p3_1 = p3_0.lerp(p4_0, t);
 | 
				
			||||||
 | 
					    let p4_1 = p4_0.lerp(p5_0, t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let p0_2 = p0_1.lerp(p1_1, t);
 | 
				
			||||||
 | 
					    let p1_2 = p1_1.lerp(p2_1, t);
 | 
				
			||||||
 | 
					    let p2_2 = p2_1.lerp(p3_1, t);
 | 
				
			||||||
 | 
					    let p3_2 = p3_1.lerp(p4_1, t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let p0_3 = p0_2.lerp(p1_2, t);
 | 
				
			||||||
 | 
					    let p1_3 = p1_2.lerp(p2_2, t);
 | 
				
			||||||
 | 
					    let p2_3 = p2_2.lerp(p3_2, t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let p0_4 = p0_3.lerp(p1_3, t);
 | 
				
			||||||
 | 
					    let p1_4 = p1_3.lerp(p2_3, t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let p0_5 = p0_4.lerp(p1_4, t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    p0_5
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn split_bezier(points: &[Pos2; 6], t: f32) -> [[Pos2; 6]; 2] {
 | 
				
			||||||
 | 
					    let [p0, p1, p2, p3, p4, p5] = *points;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let p0_0 = p0;
 | 
				
			||||||
 | 
					    let p1_0 = p1;
 | 
				
			||||||
 | 
					    let p2_0 = p2;
 | 
				
			||||||
 | 
					    let p3_0 = p3;
 | 
				
			||||||
 | 
					    let p4_0 = p4;
 | 
				
			||||||
 | 
					    let p5_0 = p5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let p0_1 = p0_0.lerp(p1_0, t);
 | 
				
			||||||
 | 
					    let p1_1 = p1_0.lerp(p2_0, t);
 | 
				
			||||||
 | 
					    let p2_1 = p2_0.lerp(p3_0, t);
 | 
				
			||||||
 | 
					    let p3_1 = p3_0.lerp(p4_0, t);
 | 
				
			||||||
 | 
					    let p4_1 = p4_0.lerp(p5_0, t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let p0_2 = p0_1.lerp(p1_1, t);
 | 
				
			||||||
 | 
					    let p1_2 = p1_1.lerp(p2_1, t);
 | 
				
			||||||
 | 
					    let p2_2 = p2_1.lerp(p3_1, t);
 | 
				
			||||||
 | 
					    let p3_2 = p3_1.lerp(p4_1, t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let p0_3 = p0_2.lerp(p1_2, t);
 | 
				
			||||||
 | 
					    let p1_3 = p1_2.lerp(p2_2, t);
 | 
				
			||||||
 | 
					    let p2_3 = p2_2.lerp(p3_2, t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let p0_4 = p0_3.lerp(p1_3, t);
 | 
				
			||||||
 | 
					    let p1_4 = p1_3.lerp(p2_3, t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let p0_5 = p0_4.lerp(p1_4, t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					        [p0_0, p0_1, p0_2, p0_3, p0_4, p0_5],
 | 
				
			||||||
 | 
					        [p0_5, p1_4, p2_3, p3_2, p4_1, p5_0],
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn hit_bezier(pos: Pos2, points: &[Pos2; 6], threshold: f32) -> bool {
 | 
				
			||||||
 | 
					    let aabb = Rect::from_points(points);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if pos.x + threshold < aabb.left() {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if pos.x - threshold > aabb.right() {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if pos.y + threshold < aabb.top() {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if pos.y - threshold > aabb.bottom() {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let samples = bezier_samples_number(points, threshold);
 | 
				
			||||||
 | 
					    if samples > 16 {
 | 
				
			||||||
 | 
					        let [points1, points2] = split_bezier(points, 0.5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return hit_bezier(pos, &points1, threshold) || hit_bezier(pos, &points2, threshold);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for i in 0..samples {
 | 
				
			||||||
 | 
					        #[allow(clippy::cast_precision_loss)]
 | 
				
			||||||
 | 
					        let t = i as f32 / (samples - 1) as f32;
 | 
				
			||||||
 | 
					        let p = sample_bezier(points, t);
 | 
				
			||||||
 | 
					        if (p - pos).length() < threshold {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn mix_colors(a: Color32, b: Color32) -> Color32 {
 | 
				
			||||||
 | 
					    let [or, og, ob, oa] = a.to_array();
 | 
				
			||||||
 | 
					    let [ir, ig, ib, ia] = b.to_array();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Color32::from_rgba_premultiplied(
 | 
				
			||||||
 | 
					        or / 2 + ir / 2,
 | 
				
			||||||
 | 
					        og / 2 + ig / 2,
 | 
				
			||||||
 | 
					        ob / 2 + ib / 2,
 | 
				
			||||||
 | 
					        oa / 2 + ia / 2,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										192
									
								
								vendor/egui-snarl/src/ui/zoom.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								vendor/egui-snarl/src/ui/zoom.rs
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,192 @@
 | 
				
			||||||
 | 
					use egui::{
 | 
				
			||||||
 | 
					    epaint::Shadow,
 | 
				
			||||||
 | 
					    style::{Interaction, ScrollStyle, Spacing, WidgetVisuals, Widgets},
 | 
				
			||||||
 | 
					    FontId, Frame, Margin, Rounding, Stroke, Style, Vec2, Visuals,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub trait Zoom {
 | 
				
			||||||
 | 
					    fn zoomed(mut self, zoom: f32) -> Self
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        Self: Copy,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        self.zoom(zoom);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for f32 {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        *self *= zoom;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for Vec2 {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        *self *= zoom;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for Rounding {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        self.nw.zoom(zoom);
 | 
				
			||||||
 | 
					        self.ne.zoom(zoom);
 | 
				
			||||||
 | 
					        self.se.zoom(zoom);
 | 
				
			||||||
 | 
					        self.sw.zoom(zoom);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for Margin {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        self.left.zoom(zoom);
 | 
				
			||||||
 | 
					        self.right.zoom(zoom);
 | 
				
			||||||
 | 
					        self.top.zoom(zoom);
 | 
				
			||||||
 | 
					        self.bottom.zoom(zoom);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for Shadow {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        self.spread = zoom;
 | 
				
			||||||
 | 
					        // self.extrusion.zoom(zoom);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for Stroke {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        self.width *= zoom;
 | 
				
			||||||
 | 
					        if self.width < 1.0 {
 | 
				
			||||||
 | 
					            self.color.gamma_multiply(self.width);
 | 
				
			||||||
 | 
					            self.width = 1.0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for WidgetVisuals {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        self.bg_stroke.zoom(zoom);
 | 
				
			||||||
 | 
					        self.rounding.zoom(zoom);
 | 
				
			||||||
 | 
					        self.fg_stroke.zoom(zoom);
 | 
				
			||||||
 | 
					        self.expansion.zoom(zoom);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for Interaction {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        self.resize_grab_radius_corner.zoom(zoom);
 | 
				
			||||||
 | 
					        self.resize_grab_radius_side.zoom(zoom);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for Widgets {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        self.noninteractive.zoom(zoom);
 | 
				
			||||||
 | 
					        self.inactive.zoom(zoom);
 | 
				
			||||||
 | 
					        self.hovered.zoom(zoom);
 | 
				
			||||||
 | 
					        self.active.zoom(zoom);
 | 
				
			||||||
 | 
					        self.open.zoom(zoom);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for Visuals {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        self.clip_rect_margin.zoom(zoom);
 | 
				
			||||||
 | 
					        self.menu_rounding.zoom(zoom);
 | 
				
			||||||
 | 
					        self.popup_shadow.zoom(zoom);
 | 
				
			||||||
 | 
					        self.resize_corner_size.zoom(zoom);
 | 
				
			||||||
 | 
					        self.selection.stroke.zoom(zoom);
 | 
				
			||||||
 | 
					        self.text_cursor.zoom(zoom);
 | 
				
			||||||
 | 
					        self.widgets.zoom(zoom);
 | 
				
			||||||
 | 
					        self.window_rounding.zoom(zoom);
 | 
				
			||||||
 | 
					        self.window_shadow.zoom(zoom);
 | 
				
			||||||
 | 
					        self.window_stroke.zoom(zoom);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for ScrollStyle {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        self.bar_inner_margin.zoom(zoom);
 | 
				
			||||||
 | 
					        self.bar_outer_margin.zoom(zoom);
 | 
				
			||||||
 | 
					        self.bar_width.zoom(zoom);
 | 
				
			||||||
 | 
					        self.floating_allocated_width.zoom(zoom);
 | 
				
			||||||
 | 
					        self.floating_width.zoom(zoom);
 | 
				
			||||||
 | 
					        self.handle_min_length.zoom(zoom);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for Spacing {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        self.button_padding.zoom(zoom);
 | 
				
			||||||
 | 
					        self.combo_height.zoom(zoom);
 | 
				
			||||||
 | 
					        self.combo_width.zoom(zoom);
 | 
				
			||||||
 | 
					        self.icon_spacing.zoom(zoom);
 | 
				
			||||||
 | 
					        self.icon_width.zoom(zoom);
 | 
				
			||||||
 | 
					        self.icon_width_inner.zoom(zoom);
 | 
				
			||||||
 | 
					        self.indent.zoom(zoom);
 | 
				
			||||||
 | 
					        self.interact_size.zoom(zoom);
 | 
				
			||||||
 | 
					        self.item_spacing.zoom(zoom);
 | 
				
			||||||
 | 
					        self.menu_margin.zoom(zoom);
 | 
				
			||||||
 | 
					        self.scroll.zoom(zoom);
 | 
				
			||||||
 | 
					        self.slider_width.zoom(zoom);
 | 
				
			||||||
 | 
					        self.text_edit_width.zoom(zoom);
 | 
				
			||||||
 | 
					        self.tooltip_width.zoom(zoom);
 | 
				
			||||||
 | 
					        self.window_margin.zoom(zoom);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for FontId {
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        self.size.zoom(zoom);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for Style {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        if let Some(font_id) = &mut self.override_font_id {
 | 
				
			||||||
 | 
					            font_id.zoom(zoom);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for font_id in self.text_styles.values_mut() {
 | 
				
			||||||
 | 
					            font_id.zoom(zoom);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        self.interaction.zoom(zoom);
 | 
				
			||||||
 | 
					        self.spacing.zoom(zoom);
 | 
				
			||||||
 | 
					        self.visuals.zoom(zoom);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> Zoom for Option<T>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    T: Zoom,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        if let Some(value) = self {
 | 
				
			||||||
 | 
					            value.zoom(zoom)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Zoom for Frame {
 | 
				
			||||||
 | 
					    #[inline(always)]
 | 
				
			||||||
 | 
					    fn zoom(&mut self, zoom: f32) {
 | 
				
			||||||
 | 
					        self.inner_margin.zoom(zoom);
 | 
				
			||||||
 | 
					        self.outer_margin.zoom(zoom);
 | 
				
			||||||
 | 
					        self.rounding.zoom(zoom);
 | 
				
			||||||
 | 
					        self.shadow.zoom(zoom);
 | 
				
			||||||
 | 
					        self.stroke.zoom(zoom);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue