#![deny(unsafe_code)] use crate::error::*; use crate::graphics_permanent::{ PermanentGraphicsState, GraphicsStateForReinit }; use crate::graphics_window_dressing::{ WindowDressing, N_SIMULTANEOUS_FRAMES }; use crate::linear_algebra::{ Vec3, Vec4, Mat4, Transformation, UniformBlock }; use std::cell::RefCell; use std::f32::consts::{ FRAC_PI_2, FRAC_PI_4, TAU }; use std::ptr::copy_nonoverlapping; use std::time::Instant; use vulkanalia::Device; 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; mod linear_algebra; struct Surreality { permanent: RefCell>, for_reinit: RefCell>, window_dressing: RefCell>, is_minimized: bool, is_reinit_queued: bool, frame_index: usize, simulation_start: Instant, frame_count: usize, last_second: u64, frame_count_last_second: 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, simulation_start: Instant::now(), frame_count: 0, last_second: 0, frame_count_last_second: 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; render_uniforms(device, &window_dressing.uniform_buffer_memory[image_index], &window_dressing.swapchain.extent, self.simulation_start)?; 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; } self.frame_count += 1; let second = self.simulation_start.elapsed().as_secs(); if second > self.last_second { let rate = (self.frame_count - self.frame_count_last_second) as f32 / (second - self.last_second) as f32; println!("{} fps", rate); self.frame_count_last_second = self.frame_count; self.last_second = second; } 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); } if let Some(for_reinit) = self.for_reinit.replace(None) { for_reinit.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 about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { if let Some(permanent) = self.permanent.borrow().as_ref() { permanent.window.request_redraw(); } } } 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) } } } #[allow(unsafe_code)] fn render_uniforms(device: &Device, device_memory: &vk::DeviceMemory, extent: &vk::Extent2D, simulation_start: Instant) -> Result<()> { let time = simulation_start.elapsed().as_secs_f32(); let scale = Vec3::new(1.0, 1.0, 1.0); let model = Transformation { rotation: Vec4::rotation_quaternion(&Vec3::new(-1.0, 0.0, 0.0), FRAC_PI_2) .rotate(&Vec3::new(0.0, 1.0, 0.0), time % TAU), translation: Vec3::new(0.0, 0.0, 0.0), }; let view = Transformation { rotation: Vec4::rotation_quaternion(&Vec3::new(1.0, 0.0, 0.0), 0.1), translation: Vec3::new(0.0, 0.0, 2.0), }; let aspect_ratio = extent.width as f32 / extent.height as f32; let projection = Mat4::perspective(FRAC_PI_4, aspect_ratio, 0.1, 10.0); let block = UniformBlock:: { scale, model, view, projection }; let size = size_of::>() as u64; let host_memory = unsafe { device.map_memory(*device_memory, 0, size, vk::MemoryMapFlags::empty()) }?; unsafe { copy_nonoverlapping(&block, host_memory.cast(), 1) }; unsafe { device.unmap_memory(*device_memory) }; Ok(()) }