#![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::look_at(&Vec3::new(0.0, -1.0, -2.0),
&Vec3::new(0.0, 0.0, 0.0),
&Vec3::new(0.0, -1.0, 0.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(())
}