Compare commits
	
		
			No commits in common. "7b7badd4d41109777a000357ef64b42d05691b84" and "29258aed7b5b496d6a005f764ef1f0916afc8d94" have entirely different histories.
		
	
	
		
			7b7badd4d4
			...
			29258aed7b
		
	
		
|  | @ -17,6 +17,7 @@ tracing = "0.1.40" | |||
| tracing-subscriber = "0.3.18" | ||||
| vk-mem = "0.4.0" | ||||
| vk-sync = "0.1.6" | ||||
| winit = "0.30.5" | ||||
| tinyvec = "1.8" | ||||
| rand = "0.8.5" | ||||
| tokio = "1.42.0" | ||||
|  | @ -24,9 +25,3 @@ tokio = "1.42.0" | |||
| futures = "0.3" | ||||
| smol = "2.0" | ||||
| rayon = "1.10" | ||||
| 
 | ||||
| winit = {version = "0.30.5", features = ["rwh_06"]} | ||||
| raw-window-handle = "0.6" | ||||
| 
 | ||||
| egui = "0.30.0" | ||||
| egui_winit_platform = "0.24.0" | ||||
|  | @ -8,6 +8,3 @@ winit = { workspace = true } | |||
| tracing = { workspace = true } | ||||
| tracing-subscriber = { workspace = true } | ||||
| renderer = { path = "../renderer" } | ||||
| 
 | ||||
| egui = { workspace = true } | ||||
| egui_winit_platform = { workspace = true } | ||||
|  |  | |||
|  | @ -12,33 +12,25 @@ use winit::{ | |||
| }; | ||||
| 
 | ||||
| struct WindowState { | ||||
|     window: Window, | ||||
|     egui_platform: egui_winit_platform::Platform, | ||||
| } | ||||
| 
 | ||||
| struct WinitState { | ||||
|     last_resize_events: BTreeMap<WindowId, PhysicalSize<u32>>, | ||||
|     window_attrs: WindowAttributes, | ||||
|     windows2: BTreeMap<WindowId, WindowState>, | ||||
|     renderer: Renderer<WindowId>, | ||||
|     window: Option<Window>, | ||||
|     windows: BTreeMap<WindowId, Window>, | ||||
|     renderer: Renderer, | ||||
| } | ||||
| 
 | ||||
| impl WinitState { | ||||
|     const BASE_WIDTH: u32 = 800; | ||||
|     const BASE_HEIGHT: u32 = 600; | ||||
|     fn new(window_title: String, display: DisplayHandle) -> WinitState { | ||||
| impl WindowState { | ||||
|     fn new(window_title: String, display: DisplayHandle) -> WindowState { | ||||
|         Self { | ||||
|             windows2: BTreeMap::new(), | ||||
|             window: None, | ||||
|             windows: BTreeMap::new(), | ||||
|             last_resize_events: BTreeMap::new(), | ||||
|             window_attrs: WindowAttributes::default() | ||||
|                 .with_title(window_title) | ||||
|                 .with_resizable(true) | ||||
|                 .with_inner_size(LogicalSize::new( | ||||
|                     Self::BASE_WIDTH, | ||||
|                     Self::BASE_HEIGHT, | ||||
|                 )), | ||||
|                 .with_inner_size(LogicalSize::new(800, 600)), | ||||
|             // TODO: pass down this error and add some kind of error handling UI or dump
 | ||||
|             renderer: Renderer::new(display.as_raw()).expect("renderer"), | ||||
|             renderer: Renderer::new(display).expect("renderer"), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -50,10 +42,10 @@ impl WinitState { | |||
|         _ = (window_id, new_size); | ||||
|         info!("TODO: implement resize events"); | ||||
|         if let Some(ctx) = self.renderer.window_contexts.get_mut(&window_id) { | ||||
|             ctx.recreate_with(Some(renderer::Extent2D { | ||||
|             ctx.recreate_swapchain(renderer::Extent2D { | ||||
|                 width: new_size.width, | ||||
|                 height: new_size.height, | ||||
|             })) | ||||
|             }) | ||||
|             .expect("swapchain recreation"); | ||||
|         } | ||||
|     } | ||||
|  | @ -64,30 +56,7 @@ impl WinitState { | |||
|             window_id = u64::from(window_id), | ||||
|             "TODO: implement draw request" | ||||
|         ); | ||||
|         if let Some(window) = self.windows2.get_mut(&window_id) { | ||||
|             // egui
 | ||||
| 
 | ||||
|             window.egui_platform.begin_frame(); | ||||
|             let output = window.egui_platform.end_frame(Some(&window.window)); | ||||
| 
 | ||||
|             let _draw_data = window | ||||
|                 .egui_platform | ||||
|                 .context() | ||||
|                 .tessellate(output.shapes, output.pixels_per_point); | ||||
| 
 | ||||
|             // rendering
 | ||||
|             self.renderer | ||||
|                 .debug_draw(&window_id, || window.window.pre_present_notify()) | ||||
|                 .expect("drawing"); | ||||
|             window.window.request_redraw(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn remove_window(&mut self, window_id: WindowId) { | ||||
|         tracing::info!(window = u64::from(window_id), "window close requested"); | ||||
| 
 | ||||
|         self.windows2.remove(&window_id); | ||||
|         self.renderer.window_contexts.remove(&window_id); | ||||
|         self.renderer.debug_draw().expect("drawing"); | ||||
|     } | ||||
| 
 | ||||
|     fn create_window( | ||||
|  | @ -120,43 +89,25 @@ impl WinitState { | |||
|             window.window_handle().expect("window handle"), | ||||
|         ); | ||||
| 
 | ||||
|         self.windows2.insert( | ||||
|             window_id, | ||||
|             WindowState { | ||||
|                 window, | ||||
|                 egui_platform: egui_winit_platform::Platform::new( | ||||
|                     egui_winit_platform::PlatformDescriptor { | ||||
|                         physical_width: size.width, | ||||
|                         physical_height: size.height, | ||||
|                         scale_factor: 1.0, | ||||
|                         ..Default::default() | ||||
|                     }, | ||||
|                 ), | ||||
|             }, | ||||
|         ); | ||||
|         self.windows.insert(window.id(), window); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ApplicationHandler for WinitState { | ||||
| impl ApplicationHandler for WindowState { | ||||
|     fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { | ||||
|         tracing::info!("winit::resumed"); | ||||
|         tracing::debug!("winit::resumed"); | ||||
| 
 | ||||
|         self.create_window(event_loop); | ||||
|     } | ||||
| 
 | ||||
|     fn about_to_wait( | ||||
|         &mut self, | ||||
|         event_loop: &winit::event_loop::ActiveEventLoop, | ||||
|         _event_loop: &winit::event_loop::ActiveEventLoop, | ||||
|     ) { | ||||
|         tracing::info!("winit::about_to_wait"); | ||||
|         for (&window, &resize) in self.last_resize_events.clone().iter() { | ||||
|             self.handle_final_resize(window, resize); | ||||
|         } | ||||
|         self.last_resize_events.clear(); | ||||
| 
 | ||||
|         if self.windows2.is_empty() { | ||||
|             event_loop.exit(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn window_event( | ||||
|  | @ -165,35 +116,27 @@ impl ApplicationHandler for WinitState { | |||
|         window_id: winit::window::WindowId, | ||||
|         event: winit::event::WindowEvent, | ||||
|     ) { | ||||
|         // if !matches!(event, winit::event::WindowEvent::Resized(_)) {
 | ||||
|         //     if let Some(resize) = self.last_resize_events.remove(&window_id) {
 | ||||
|         //         self.handle_final_resize(window_id, resize);
 | ||||
|         //     }
 | ||||
|         // }
 | ||||
| 
 | ||||
|         if let Some(window) = self.windows2.get_mut(&window_id) { | ||||
|             window.egui_platform.handle_event(&event); | ||||
|         if !matches!(event, winit::event::WindowEvent::Resized(_)) { | ||||
|             if let Some(resize) = self.last_resize_events.remove(&window_id) { | ||||
|                 self.handle_final_resize(window_id, resize); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         match event { | ||||
|             winit::event::WindowEvent::Resized(physical_size) => { | ||||
|                 _ = self.last_resize_events.insert(window_id, physical_size); | ||||
|             } | ||||
|             winit::event::WindowEvent::CloseRequested | ||||
|             | winit::event::WindowEvent::KeyboardInput { | ||||
|                 event: | ||||
|                     winit::event::KeyEvent { | ||||
|                         physical_key: | ||||
|                             winit::keyboard::PhysicalKey::Code( | ||||
|                                 winit::keyboard::KeyCode::KeyQ, | ||||
|                             ), | ||||
|                         state: ElementState::Pressed, | ||||
|                         repeat: false, | ||||
|                         .. | ||||
|                     }, | ||||
|                 .. | ||||
|             } => { | ||||
|                 self.remove_window(window_id); | ||||
|             winit::event::WindowEvent::CloseRequested => { | ||||
|                 tracing::info!( | ||||
|                     window = u64::from(window_id), | ||||
|                     "window close requested" | ||||
|                 ); | ||||
| 
 | ||||
|                 self.windows.remove(&window_id); | ||||
|                 self.renderer.window_contexts.remove(&window_id); | ||||
| 
 | ||||
|                 if self.windows.is_empty() { | ||||
|                     event_loop.exit(); | ||||
|                 } | ||||
|             } | ||||
|             winit::event::WindowEvent::KeyboardInput { | ||||
|                 event: | ||||
|  | @ -232,6 +175,9 @@ impl ApplicationHandler for WinitState { | |||
|             } | ||||
|             winit::event::WindowEvent::RedrawRequested => { | ||||
|                 self.handle_draw_request(window_id); | ||||
|                 if let Some(window) = self.windows.get(&window_id) { | ||||
|                     window.request_redraw(); | ||||
|                 } | ||||
|             } | ||||
|             _ => {} // unhandled event
 | ||||
|         } | ||||
|  | @ -241,9 +187,8 @@ impl ApplicationHandler for WinitState { | |||
| fn main() { | ||||
|     tracing_subscriber::fmt().init(); | ||||
|     let ev = EventLoop::new().unwrap(); | ||||
|     ev.set_control_flow(winit::event_loop::ControlFlow::Poll); | ||||
| 
 | ||||
|     let display = ev.display_handle().expect("display handle"); | ||||
|     let mut game = WinitState::new("Vidya".to_owned(), display); | ||||
|     let mut game = WindowState::new("Vidya".to_owned(), display); | ||||
|     ev.run_app(&mut game).unwrap(); | ||||
| } | ||||
|  |  | |||
|  | @ -17,11 +17,4 @@ tracing = "0.1.40" | |||
| tracing-subscriber = "0.3.18" | ||||
| vk-mem = "0.4.0" | ||||
| vk-sync = "0.1.6" | ||||
| crossbeam = "0.8.4" | ||||
| parking_lot = "0.12.3" | ||||
| smol.workspace = true | ||||
| tracing-test = "0.2.5" | ||||
| 
 | ||||
| raw-window-handle = { workspace = true } | ||||
| egui = { workspace = true } | ||||
| egui_winit_platform = { workspace = true } | ||||
| winit = "0.30.5" | ||||
|  |  | |||
|  | @ -1,14 +1,64 @@ | |||
| use std::{future::Future, marker::PhantomData, sync::Arc}; | ||||
| use std::{future::Future, marker::PhantomData}; | ||||
| 
 | ||||
| use crate::sync::{self, FenceFuture}; | ||||
| 
 | ||||
| use super::{Device, Queue}; | ||||
| use super::{Device2 as Device, Queue}; | ||||
| use ash::{prelude::*, vk}; | ||||
| 
 | ||||
| pub struct FenceFuture<'a> { | ||||
|     device: Device, | ||||
|     fence: vk::Fence, | ||||
|     _pd: PhantomData<&'a ()>, | ||||
| } | ||||
| 
 | ||||
| impl Drop for FenceFuture<'_> { | ||||
|     fn drop(&mut self) { | ||||
|         unsafe { self.device.dev().destroy_fence(self.fence, None) }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl FenceFuture<'_> { | ||||
|     /// Unsafe because `fence` must not be destroyed while this future is live.
 | ||||
|     pub unsafe fn from_fence(device: Device, fence: vk::Fence) -> Self { | ||||
|         Self { | ||||
|             device, | ||||
|             fence, | ||||
|             _pd: PhantomData, | ||||
|         } | ||||
|     } | ||||
|     pub fn block_until(&self) -> VkResult<()> { | ||||
|         unsafe { | ||||
|             self.device | ||||
|                 .dev() | ||||
|                 .wait_for_fences(&[self.fence], true, u64::MAX) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Future for FenceFuture<'_> { | ||||
|     type Output = (); | ||||
| 
 | ||||
|     fn poll( | ||||
|         self: std::pin::Pin<&mut Self>, | ||||
|         cx: &mut std::task::Context<'_>, | ||||
|     ) -> std::task::Poll<Self::Output> { | ||||
|         let signaled = unsafe { | ||||
|             self.device | ||||
|                 .dev() | ||||
|                 .get_fence_status(self.fence) | ||||
|                 .unwrap_or(false) | ||||
|         }; | ||||
| 
 | ||||
|         if signaled { | ||||
|             std::task::Poll::Ready(()) | ||||
|         } else { | ||||
|             tokio::task::spawn_blocking(move || {}); | ||||
|             std::task::Poll::Pending | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct SingleUseCommandPool { | ||||
|     device: Device, | ||||
|     pool: vk::CommandPool, | ||||
|     queue: Queue, | ||||
| } | ||||
| 
 | ||||
| impl Drop for SingleUseCommandPool { | ||||
|  | @ -20,27 +70,15 @@ impl Drop for SingleUseCommandPool { | |||
| } | ||||
| 
 | ||||
| impl SingleUseCommandPool { | ||||
|     pub fn new(device: Device, queue: Queue) -> VkResult<Arc<Self>> { | ||||
|     pub fn new(device: Device, family_index: u32) -> VkResult<Self> { | ||||
|         let pool_info = vk::CommandPoolCreateInfo::default() | ||||
|             .queue_family_index(queue.family()) | ||||
|             .queue_family_index(family_index) | ||||
|             .flags(vk::CommandPoolCreateFlags::TRANSIENT); | ||||
| 
 | ||||
|         let pool = | ||||
|             unsafe { device.dev().create_command_pool(&pool_info, None)? }; | ||||
| 
 | ||||
|         Ok(Arc::new(Self { | ||||
|             device, | ||||
|             pool, | ||||
|             queue, | ||||
|         })) | ||||
|     } | ||||
| 
 | ||||
|     pub fn alloc(self: &Arc<Self>) -> VkResult<SingleUseCommand> { | ||||
|         SingleUseCommand::new(self.device.clone(), self.clone()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn queue(&self) -> &Queue { | ||||
|         &self.queue | ||||
|         Ok(Self { device, pool }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn pool(&self) -> vk::CommandPool { | ||||
|  | @ -50,7 +88,7 @@ impl SingleUseCommandPool { | |||
| 
 | ||||
| pub struct SingleUseCommand { | ||||
|     device: Device, | ||||
|     pool: Arc<SingleUseCommandPool>, | ||||
|     pool: vk::CommandPool, | ||||
|     buffer: vk::CommandBuffer, | ||||
| } | ||||
| 
 | ||||
|  | @ -59,28 +97,23 @@ impl Drop for SingleUseCommand { | |||
|         unsafe { | ||||
|             self.device | ||||
|                 .dev() | ||||
|                 .free_command_buffers(self.pool.pool(), &[self.buffer]) | ||||
|                 .free_command_buffers(self.pool, &[self.buffer]) | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SingleUseCommand { | ||||
|     pub fn new( | ||||
|         device: Device, | ||||
|         pool: Arc<SingleUseCommandPool>, | ||||
|     ) -> VkResult<Self> { | ||||
|     pub fn new(device: Device, pool: vk::CommandPool) -> VkResult<Self> { | ||||
|         let buffer = unsafe { | ||||
|             let alloc_info = vk::CommandBufferAllocateInfo::default() | ||||
|                 .command_buffer_count(1) | ||||
|                 .command_pool(pool.pool()) | ||||
|                 .command_pool(pool) | ||||
|                 .level(vk::CommandBufferLevel::PRIMARY); | ||||
|             let buffer = device.dev().allocate_command_buffers(&alloc_info)?[0]; | ||||
| 
 | ||||
|             device.dev().begin_command_buffer( | ||||
|                 buffer, | ||||
|                 &vk::CommandBufferBeginInfo::default() | ||||
|                     .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), | ||||
|             )?; | ||||
|             let begin_info = vk::CommandBufferBeginInfo::default() | ||||
|                 .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT); | ||||
|             device.dev().begin_command_buffer(buffer, &begin_info)?; | ||||
| 
 | ||||
|             buffer | ||||
|         }; | ||||
|  | @ -91,16 +124,16 @@ impl SingleUseCommand { | |||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn buffer(&self) -> vk::CommandBuffer { | ||||
|     pub fn command_buffer(&self) -> vk::CommandBuffer { | ||||
|         self.buffer | ||||
|     } | ||||
| 
 | ||||
|     pub fn submit_fence( | ||||
|     fn submit_fence( | ||||
|         &self, | ||||
|         queue: Queue, | ||||
|         wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>, | ||||
|         signal: Option<vk::Semaphore>, | ||||
|         fence: Option<vk::Fence>, | ||||
|     ) -> VkResult<()> { | ||||
|     ) -> VkResult<vk::Fence> { | ||||
|         unsafe { self.device.dev().end_command_buffer(self.buffer)? }; | ||||
| 
 | ||||
|         let buffers = [self.buffer]; | ||||
|  | @ -121,39 +154,43 @@ impl SingleUseCommand { | |||
|                 .wait_dst_stage_mask(core::slice::from_ref(stage)); | ||||
|         } | ||||
| 
 | ||||
|         let fence = fence.unwrap_or(vk::Fence::null()); | ||||
|         self.pool.queue().with_locked(|queue| unsafe { | ||||
|         let fence = { | ||||
|             let create_info = vk::FenceCreateInfo::default(); | ||||
|             unsafe { self.device.dev().create_fence(&create_info, None)? } | ||||
|         }; | ||||
| 
 | ||||
|         queue.with_locked(|queue| unsafe { | ||||
|             self.device.dev().queue_submit(queue, &[submit_info], fence) | ||||
|         })?; | ||||
|         tracing::info!( | ||||
|             "submitted queue {:?} and fence {:?}", | ||||
|             self.pool.queue(), | ||||
|             fence | ||||
|         ); | ||||
| 
 | ||||
|         Ok(()) | ||||
|         Ok(fence) | ||||
|     } | ||||
| 
 | ||||
|     pub fn submit_async<'a>( | ||||
|         &'a self, | ||||
|         queue: Queue, | ||||
|         wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>, | ||||
|         signal: Option<vk::Semaphore>, | ||||
|         fence: Arc<sync::Fence>, | ||||
|     ) -> VkResult<FenceFuture<'a>> { | ||||
|         let device = self.device.clone(); | ||||
|         self.submit_fence(wait, signal, Some(fence.fence()))?; | ||||
|         let fence = self.submit_fence(queue, wait, signal)?; | ||||
| 
 | ||||
|         Ok(unsafe { FenceFuture::new(fence) }) | ||||
|         Ok(unsafe { FenceFuture::from_fence(device, fence) }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn submit_blocking( | ||||
|         self, | ||||
|         queue: Queue, | ||||
|         wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>, | ||||
|         signal: Option<vk::Semaphore>, | ||||
|     ) -> VkResult<()> { | ||||
|         let fence = Arc::new(sync::Fence::create(self.device.clone())?); | ||||
|         let future = self.submit_async(wait, signal, fence)?; | ||||
|         future.block(); | ||||
|         let device = self.device.clone(); | ||||
|         let fence = self.submit_fence(queue, wait, signal)?; | ||||
| 
 | ||||
|         unsafe { | ||||
|             device.dev().wait_for_fences(&[fence], true, u64::MAX)?; | ||||
|             device.dev().destroy_fence(fence, None); | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | @ -163,12 +200,6 @@ mod tests { | |||
|     use super::*; | ||||
| 
 | ||||
|     async fn async_submit(cmd: SingleUseCommand, queue: Queue) { | ||||
|         cmd.submit_async( | ||||
|             None, | ||||
|             None, | ||||
|             Arc::new(sync::Fence::create(cmd.device.clone()).unwrap()), | ||||
|         ) | ||||
|         .unwrap() | ||||
|         .await; | ||||
|         cmd.submit_async(queue, None, None).unwrap().await; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| use super::{Device, Queue, VkAllocator}; | ||||
| use super::{Device2 as Device, Queue, VkAllocator}; | ||||
| use ash::{prelude::*, vk}; | ||||
| use vk_mem::Alloc; | ||||
| 
 | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1,27 +0,0 @@ | |||
| use ash::vk; | ||||
| 
 | ||||
| struct Rgba([f32;4]); | ||||
| 
 | ||||
| enum LoadOp { | ||||
|     Clear(Rgba), | ||||
|     Load, | ||||
|     DontCare, | ||||
| } | ||||
| 
 | ||||
| enum StoreOp { | ||||
|     DontCare,Store, | ||||
| } | ||||
| 
 | ||||
| struct AttachmentInfo { | ||||
|     size: glam::UVec2, | ||||
|     format: vk::Format, | ||||
|     load: LoadOp, | ||||
|     store: StoreOp, | ||||
| } | ||||
| 
 | ||||
| struct Texture { | ||||
|     texture: vk::Image, | ||||
| } | ||||
| 
 | ||||
| pub struct RenderGraph { | ||||
| } | ||||
|  | @ -1,239 +1,11 @@ | |||
| use std::{ | ||||
|     future::Future, | ||||
|     marker::PhantomData, | ||||
|     sync::{atomic::AtomicU32, Arc}, | ||||
|     time::Duration, | ||||
| }; | ||||
| 
 | ||||
| use super::Device; | ||||
| use super::Device2 as Device; | ||||
| use ash::{prelude::*, vk}; | ||||
| use crossbeam::channel::{Receiver, Sender}; | ||||
| 
 | ||||
| type Message = (SyncPrimitive, std::task::Waker); | ||||
| 
 | ||||
| pub struct SyncThreadpool { | ||||
|     channel: (Sender<Message>, Receiver<Message>), | ||||
|     timeout: u64, | ||||
|     thread_dies_after: Duration, | ||||
|     max_threads: u32, | ||||
|     num_threads: Arc<AtomicU32>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| enum SyncPrimitive { | ||||
|     Fence(Arc<Fence>), | ||||
|     // actually, I think this is an awful idea because I would have to hold a
 | ||||
|     // lock on all queues.
 | ||||
|     DeviceIdle(Device), | ||||
| } | ||||
| 
 | ||||
| impl SyncThreadpool { | ||||
|     pub fn new() -> SyncThreadpool { | ||||
|         Self::with_max_threads(512) | ||||
|     } | ||||
| 
 | ||||
|     pub fn with_max_threads(max_threads: u32) -> SyncThreadpool { | ||||
|         Self { | ||||
|             // 0-capacity channel to ensure immediate consumption of fences
 | ||||
|             channel: crossbeam::channel::bounded(0), | ||||
|             max_threads, | ||||
|             num_threads: Arc::new(AtomicU32::new(0)), | ||||
|             timeout: u64::MAX, | ||||
|             thread_dies_after: Duration::from_secs(5), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn try_spawn_thread(&self) -> Option<()> { | ||||
|         use std::sync::atomic::Ordering; | ||||
|         match self.num_threads.fetch_update( | ||||
|             Ordering::Release, | ||||
|             Ordering::Acquire, | ||||
|             |i| { | ||||
|                 if i < self.max_threads { | ||||
|                     Some(i + 1) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             }, | ||||
|         ) { | ||||
|             Ok(tid) => { | ||||
|                 struct SyncThread { | ||||
|                     timeout: u64, | ||||
|                     thread_dies_after: Duration, | ||||
|                     num_threads: Arc<AtomicU32>, | ||||
|                     rx: Receiver<Message>, | ||||
|                 } | ||||
| 
 | ||||
|                 impl SyncThread { | ||||
|                     fn run(self, barrier: Arc<std::sync::Barrier>) { | ||||
|                         tracing::info!("spawned new sync thread"); | ||||
|                         barrier.wait(); | ||||
|                         while let Ok((sync, waker)) = | ||||
|                             self.rx.recv_timeout(self.thread_dies_after) | ||||
|                         { | ||||
|                             tracing::info!( | ||||
|                                 "received ({:?}, {:?})", | ||||
|                                 sync, | ||||
|                                 waker | ||||
|                             ); | ||||
|                             loop { | ||||
|                                 let wait_result = match &sync { | ||||
|                                     SyncPrimitive::Fence(fence) => { | ||||
|                                         fence.wait_on(Some(self.timeout)) | ||||
|                                     } | ||||
|                                     SyncPrimitive::DeviceIdle(device) => { | ||||
|                                         device.wait_idle() | ||||
|                                     } | ||||
|                                 }; | ||||
| 
 | ||||
|                                 match wait_result { | ||||
|                                     Ok(_) => { | ||||
|                                         waker.wake(); | ||||
|                                         break; | ||||
|                                     } | ||||
|                                     Err(vk::Result::TIMEOUT) => {} | ||||
|                                     Err(err) => { | ||||
|                                         tracing::error!( | ||||
|                                             "failed to wait on {sync:?} in waiter thread: {err}" | ||||
|                     ); | ||||
|                                         break; | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
| 
 | ||||
|                         // because I don't want some thread to not spawn as soon as this one exists
 | ||||
|                         self.num_threads.fetch_sub(1, Ordering::AcqRel); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 let thread = SyncThread { | ||||
|                     timeout: self.timeout, | ||||
|                     thread_dies_after: self.thread_dies_after, | ||||
|                     num_threads: self.num_threads.clone(), | ||||
|                     rx: self.channel.1.clone(), | ||||
|                 }; | ||||
| 
 | ||||
|                 let barrier = Arc::new(std::sync::Barrier::new(2)); | ||||
|                 std::thread::Builder::new() | ||||
|                     .name(format!("fence-waiter-{tid}")) | ||||
|                     .spawn({ | ||||
|                         let barrier = barrier.clone(); | ||||
|                         move || { | ||||
|                             thread.run(barrier); | ||||
|                         } | ||||
|                     }); | ||||
|                 barrier.wait(); | ||||
|                 Some(()) | ||||
|             } | ||||
|             Err(_) => { | ||||
|                 tracing::error!( | ||||
|                     "sync-threadpool exceeded local thread limit ({})", | ||||
|                     self.max_threads | ||||
|                 ); | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn spawn_waiter(&self, fence: Arc<Fence>, waker: std::task::Waker) { | ||||
|         use std::sync::atomic::Ordering; | ||||
| 
 | ||||
|         let mut msg = (SyncPrimitive::Fence(fence), waker); | ||||
|         while let Err(err) = self.channel.0.try_send(msg) { | ||||
|             match err { | ||||
|                 crossbeam::channel::TrySendError::Full(msg2) => { | ||||
|                     msg = msg2; | ||||
|                     self.try_spawn_thread(); | ||||
|                 } | ||||
|                 crossbeam::channel::TrySendError::Disconnected(_) => { | ||||
|                     //tracing::error!("sync-threadpool channel disconnected?");
 | ||||
|                     unreachable!() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct Semaphore { | ||||
|     device: Device, | ||||
|     inner: vk::Semaphore, | ||||
| } | ||||
| 
 | ||||
| pub struct Fence { | ||||
|     dev: Device, | ||||
|     fence: vk::Fence, | ||||
| } | ||||
| 
 | ||||
| impl std::fmt::Debug for Fence { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("Fence").field("fence", &self.fence).finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Drop for Fence { | ||||
|     fn drop(&mut self) { | ||||
|         unsafe { | ||||
|             self.dev.dev().destroy_fence(self.fence, None); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Fence { | ||||
|     unsafe fn new(dev: Device, fence: vk::Fence) -> Fence { | ||||
|         Self { dev, fence } | ||||
|     } | ||||
|     pub fn create(dev: Device) -> VkResult<Fence> { | ||||
|         unsafe { | ||||
|             Ok(Self::new( | ||||
|                 dev.clone(), | ||||
|                 dev.dev() | ||||
|                     .create_fence(&vk::FenceCreateInfo::default(), None)?, | ||||
|             )) | ||||
|         } | ||||
|     } | ||||
|     pub fn create_signaled(dev: Device) -> VkResult<Fence> { | ||||
|         unsafe { | ||||
|             Ok(Self::new( | ||||
|                 dev.clone(), | ||||
|                 dev.dev().create_fence( | ||||
|                     &vk::FenceCreateInfo::default() | ||||
|                         .flags(vk::FenceCreateFlags::SIGNALED), | ||||
|                     None, | ||||
|                 )?, | ||||
|             )) | ||||
|         } | ||||
|     } | ||||
|     pub fn wait_on(&self, timeout: Option<u64>) -> Result<(), vk::Result> { | ||||
|         use core::slice::from_ref; | ||||
|         unsafe { | ||||
|             self.dev.dev().wait_for_fences( | ||||
|                 from_ref(&self.fence), | ||||
|                 true, | ||||
|                 timeout.unwrap_or(u64::MAX), | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
|     pub fn fence(&self) -> vk::Fence { | ||||
|         self.fence | ||||
|     } | ||||
|     pub fn is_signaled(&self) -> bool { | ||||
|         unsafe { self.dev.dev().get_fence_status(self.fence).unwrap_or(false) } | ||||
|     } | ||||
|     pub fn reset(&self) -> Result<(), vk::Result> { | ||||
|         unsafe { | ||||
|             self.dev | ||||
|                 .dev() | ||||
|                 .reset_fences(core::slice::from_ref(&self.fence)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl AsRef<vk::Fence> for Fence { | ||||
|     fn as_ref(&self) -> &vk::Fence { | ||||
|         todo!() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Semaphore { | ||||
|     pub fn new(device: Device) -> VkResult<Self> { | ||||
|         let mut type_info = vk::SemaphoreTypeCreateInfo::default() | ||||
|  | @ -268,50 +40,3 @@ impl Drop for Semaphore { | |||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct FenceFuture<'a> { | ||||
|     fence: Arc<Fence>, | ||||
|     // lifetime parameter to prevent release of resources while future is pendign
 | ||||
|     _pd: PhantomData<&'a ()>, | ||||
| } | ||||
| 
 | ||||
| impl FenceFuture<'_> { | ||||
|     /// Unsafe because `fence` must not be destroyed while this future is live.
 | ||||
|     pub unsafe fn from_fence(device: Device, fence: vk::Fence) -> Self { | ||||
|         Self { | ||||
|             fence: Arc::new(Fence::new(device, fence)), | ||||
|             _pd: PhantomData, | ||||
|         } | ||||
|     } | ||||
|     pub fn new(fence: Arc<Fence>) -> Self { | ||||
|         Self { | ||||
|             fence, | ||||
|             _pd: PhantomData, | ||||
|         } | ||||
|     } | ||||
|     pub fn block(&self) -> VkResult<()> { | ||||
|         self.fence.wait_on(None)?; | ||||
|         self.fence.reset() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Future for FenceFuture<'_> { | ||||
|     type Output = (); | ||||
| 
 | ||||
|     fn poll( | ||||
|         self: std::pin::Pin<&mut Self>, | ||||
|         cx: &mut std::task::Context<'_>, | ||||
|     ) -> std::task::Poll<Self::Output> { | ||||
|         if self.fence.is_signaled() { | ||||
|             tracing::info!("fence ({:?}) is signaled", self.fence); | ||||
|             _ = self.fence.reset(); | ||||
|             std::task::Poll::Ready(()) | ||||
|         } else { | ||||
|             self.fence | ||||
|                 .dev | ||||
|                 .sync_threadpool() | ||||
|                 .spawn_waiter(self.fence.clone(), cx.waker().clone()); | ||||
|             std::task::Poll::Pending | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue