#![deny(unsafe_code)] use crate::error::*; use crate::graphics_permanent::{ PermanentGraphicsState, GraphicsStateForReinit }; use crate::graphics_window_dressing::{ WindowDressing, N_SIMULTANEOUS_FRAMES }; use std::cell::RefCell; use vulkanalia::vk::{ self, Handle, HasBuilder, DeviceV1_0, KhrSwapchainExtensionDeviceCommands }; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::{ ActiveEventLoop, EventLoop }; use winit::window::WindowId; mod error; mod graphics_permanent; mod graphics_window_dressing; struct Surreality { permanent: RefCell>, for_reinit: RefCell>, window_dressing: RefCell>, is_minimized: bool, is_reinit_queued: bool, frame_index: usize, } impl Surreality { fn new() -> Self { Surreality { permanent: RefCell::new(None), for_reinit: RefCell::new(None), window_dressing: RefCell::new(None), is_minimized: false, is_reinit_queued: false, frame_index: 0, } } #[allow(unsafe_code)] fn init(&mut self, event_loop: &ActiveEventLoop) -> Result<()> { let (permanent, for_reinit, enable_swapchain) = PermanentGraphicsState::new(event_loop)?; if enable_swapchain.0 { *self.window_dressing.get_mut() = Some(WindowDressing::new(&permanent, &for_reinit)?); } *self.permanent.get_mut() = Some(permanent); *self.for_reinit.get_mut() = Some(for_reinit); Ok(()) } fn reinit(&mut self) -> Result<()> { if let Some(permanent) = self.permanent.borrow().as_ref() && let Some(for_reinit) = self.for_reinit.borrow().as_ref() && let Some(window_dressing) = self.window_dressing.borrow_mut().as_mut() { window_dressing.reinit(permanent, for_reinit)?; } Ok(()) } fn do_frame(&mut self, window_id: WindowId) -> Result<()> { if !self.is_minimized { if self.render(window_id)? || self.is_reinit_queued { self.is_reinit_queued = false; self.reinit()?; } } Ok(()) } // This returns true if, and only if, the window dressing should be // reinitialized. #[allow(unsafe_code)] fn render(&mut self, window_id: WindowId) -> Result { if let Some(permanent) = self.permanent.borrow().as_ref() && let Some(window_dressing) = self.window_dressing.borrow_mut().as_mut() && window_id == permanent.window.id() { let device = &permanent.device; let frame_index = self.frame_index; let concurrency = &mut window_dressing.concurrency; let image_available_semaphore = &concurrency.image_available_semaphores[frame_index]; let rendering_finished_semaphore = &concurrency.rendering_finished_semaphores[frame_index]; let frame_fence = &concurrency.frame_fences[frame_index]; unsafe { device.wait_for_fences(&[*frame_fence], true, u64::MAX) }?; let image_index = match unsafe { device.acquire_next_image_khr(window_dressing.swapchain.swapchain, u64::MAX, *image_available_semaphore, vk::Fence::null()) } { Ok((image_index, _)) => image_index as usize, Err(vk::ErrorCode::OUT_OF_DATE_KHR) => return Ok(true), Err(e) => return Err(Error::from(e)), }; let image_fence = &concurrency.image_fences[image_index]; if !image_fence.is_null() { unsafe { device.wait_for_fences(&[*image_fence], true, u64::MAX) }?; } concurrency.image_fences[image_index] = *frame_fence; let first_semaphores = [*image_available_semaphore]; let second_semaphores = [*rendering_finished_semaphore]; let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; let command_buffers = [window_dressing.command_buffers[image_index]]; let submit_info = vk::SubmitInfo::builder() .wait_semaphores(&first_semaphores) .wait_dst_stage_mask(&wait_stages) .command_buffers(&command_buffers) .signal_semaphores(&second_semaphores); unsafe { device.reset_fences(&[*frame_fence]) }?; unsafe { device.queue_submit(permanent.graphics_queue, &[submit_info], *frame_fence) }?; let swapchains = [window_dressing.swapchain.swapchain]; let image_indices = [image_index as u32]; let present_info = vk::PresentInfoKHR::builder() .wait_semaphores(&second_semaphores) .swapchains(&swapchains) .image_indices(&image_indices); match unsafe { device.queue_present_khr(permanent.presentation_queue, &present_info) } { Ok(vk::SuccessCode::SUBOPTIMAL_KHR) => return Ok(true), Err(vk::ErrorCode::OUT_OF_DATE_KHR) => return Ok(true), Err(e) => return Err(Error::from(e)), Ok(_) => (), } self.frame_index = (frame_index + 1) % N_SIMULTANEOUS_FRAMES; } Ok(false) } } impl Drop for Surreality { #[allow(unsafe_code)] fn drop(&mut self) { if let Some(permanent) = self.permanent.replace(None) { unsafe { permanent.device.device_wait_idle() }.unwrap(); if let Some(window_dressing) = self.window_dressing.replace(None) { window_dressing.destroy(&permanent.device); } permanent.destroy(); } } } impl ApplicationHandler for Surreality { fn resumed(&mut self, event_loop: &ActiveEventLoop) { ignore_errors(move || { self.init(event_loop)?; Ok(()) }); } fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) { match event { WindowEvent::RedrawRequested => { if !event_loop.exiting() { if let Err(e) = self.do_frame(window_id) { eprintln!("Error: {}", e); } } } WindowEvent::CloseRequested => { event_loop.exit(); } WindowEvent::Resized(size) => { if size.width == 0 || size.height == 0 { self.is_minimized = true; } else { self.is_minimized = false; self.is_reinit_queued = true; if let Err(e) = self.do_frame(window_id) { eprintln!("Error: {}", e); } } } _ => { } } } } fn main() -> std::process::ExitCode { let body: fn() -> Result<()> = || { let event_loop = EventLoop::new()?; let mut surreality = Surreality::new(); event_loop.run_app(&mut surreality)?; Ok(()) }; match body() { Ok(()) => std::process::ExitCode::SUCCESS, Err(e) => { eprintln!("Error: {}", e); std::process::ExitCode::from(1) } } }