#![allow( dead_code, unused_variables, clippy::too_many_arguments, clippy::unnecessary_wraps )] use anyhow::{anyhow, Result}; use log::*; use scene::generators; use winit::dpi::{LogicalSize, LogicalPosition}; use winit::event::{ElementState, Event, WindowEvent}; use winit::event_loop::EventLoop; use winit::keyboard::NamedKey; use winit::window::{Window, WindowBuilder}; use vulkanalia::loader::{LibloadingLoader, LIBRARY}; use vulkanalia::window as vk_window; use vulkanalia::prelude::v1_0::*; use vulkanalia::Version; use vulkanalia::bytecode::Bytecode; use std::collections::HashSet; use std::ffi::CStr; use std::os::raw::c_void; // extension imports use vulkanalia::vk::ExtDebugUtilsExtension; use vulkanalia::vk::KhrSurfaceExtension; use vulkanalia::vk::KhrSwapchainExtension; use cgmath::Matrix; use std::mem::size_of; use std::ptr::copy_nonoverlapping as memcpy; use std::time::Instant; use crate::vertex::VertexContainer; pub mod app_data; pub mod errors; pub mod swapchain; pub mod queue_family_indices; pub mod vertex; pub mod buffer; pub mod image; pub mod command_buffer; pub mod depth_buffer; pub mod load_model; pub mod scene; pub mod primitives; const PORTABILITY_MACOS_VERSION: Version = Version::new(1, 3, 216); const VALIDATION_ENABLED: bool = cfg!(debug_assertions); const VALIDATION_LAYER: vk::ExtensionName = vk::ExtensionName::from_bytes(b"VK_LAYER_KHRONOS_validation"); const DEVICE_EXTENSIONS: &[vk::ExtensionName] = &[ vk::KHR_SWAPCHAIN_EXTENSION.name ]; const MAX_FRAMES_IN_FLIGHT: usize = 30; fn main() -> Result<()> { pretty_env_logger::init(); // Window let event_loop = EventLoop::new()?; let window = WindowBuilder::new() .with_title("Vulkan Tutorial (Rust)") .with_inner_size(LogicalSize::new(1024, 768)) .build(&event_loop)?; //window.set_cursor_visible(false); // event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll); // App let mut app = unsafe { App::create(&window)? }; event_loop.run(move |event, elwt| { match event { // Request a redraw when all events were processed. Event::AboutToWait => window.request_redraw(), Event::WindowEvent { event, .. } => match event { // Render a frame if our Vulkan app is not being destroyed. WindowEvent::RedrawRequested if !elwt.exiting() && !app.minimized => unsafe { app.render(&window) }.unwrap(), // Destroy our Vulkan app. WindowEvent::CloseRequested => { elwt.exit(); unsafe { app.device.device_wait_idle().unwrap(); } unsafe { app.destroy(); } }, WindowEvent::Resized(size) => { if size.width == 0 || size.height == 0 { app.minimized = true; } else { app.minimized = false; app.resized = true; } }, WindowEvent::CursorMoved { device_id, position } => { let log_pos: LogicalPosition<f32> = position.to_logical(window.scale_factor()); if app.last_pos.x != -1.0 { app.cam_angle_x += ((log_pos.x - (window.inner_size().width as f32 / 2.0)) / (window.inner_size().width as f32)) * 180.0; app.cam_angle_y += ((log_pos.y - (window.inner_size().height as f32 / 2.0)) / (window.inner_size().height as f32)) * 180.0; if app.cam_angle_x >= 360.0 { app.cam_angle_x -= 360.0; } if app.cam_angle_x <= -360.0 { app.cam_angle_x += 360.0; } app.cam_angle_y = app.cam_angle_y.max(-90.0).min(90.0); } let cursor_res = window.set_cursor_position(LogicalPosition::new(window.inner_size().width / 2, window.inner_size().height / 2)); if cursor_res.is_err() { println!("Attempted to move cursor while not in possession") } app.last_pos = LogicalPosition::new(window.inner_size().width as f32 / 2 as f32, window.inner_size().height as f32 / 2 as f32); }, WindowEvent::KeyboardInput { device_id, event, is_synthetic } => { if event.logical_key == "w" { app.cur_pos += app.view_direction * 0.1; } if event.logical_key == "s" { app.cur_pos -= app.view_direction * 0.1; } if event.logical_key == "a" { app.cur_pos -= app.view_direction.cross(vertex::Vec3::new(0.0, 0.0, 1.0)) * 0.1; } if event.logical_key == "d" { app.cur_pos += app.view_direction.cross(vertex::Vec3::new(0.0, 0.0, 1.0)) * 0.1; } if event.logical_key == "f" && event.state == ElementState::Pressed && event.repeat == false { app.show_frame_rate = !app.show_frame_rate; } if event.logical_key == "+" && event.state == ElementState::Pressed && event.repeat == false { app.data.diffuse_raster_steps += 1; app.scene_handler.rt_memory[2] = app.data.diffuse_raster_steps; app.synchronized = 0; println!("Set diffuse tracing raster size to {}", app.scene_handler.rt_memory[2]); } if event.logical_key == "-" && event.state == ElementState::Pressed && event.repeat == false { app.data.diffuse_raster_steps = (app.data.diffuse_raster_steps).max(1) - 1; app.scene_handler.rt_memory[2] = app.data.diffuse_raster_steps; app.synchronized = 0; println!("Set diffuse tracing raster size to {}", app.scene_handler.rt_memory[2]); } if event.logical_key == NamedKey::Escape { elwt.exit(); unsafe { app.device.device_wait_idle().unwrap(); } unsafe { app.destroy(); } } }, _ => {} } _ => {} } })?; Ok(()) } /// Our Vulkan app. #[derive(Clone, Debug)] struct App { entry: Entry, instance: Instance, data: app_data::AppData, device: Device, frame: usize, resized: bool, minimized: bool, start: Instant, cam_angle_x: f32, cam_angle_y: f32, last_pos: LogicalPosition<f32>, view_direction: vertex::Vec3, cur_pos: cgmath::Point3<f32>, scene_handler: scene::Scene, show_frame_rate: bool, synchronized: usize, } impl App { /// Creates our Vulkan app. unsafe fn create(window: &Window) -> Result<Self> { let loader = LibloadingLoader::new(LIBRARY)?; let entry = Entry::new(loader).map_err(|b| anyhow!("{}", b))?; let mut data = app_data::AppData::default(); data.use_geometry_shader = false; data.num_lights_per_volume = 5; data.min_light_weight = 0.0001; data.max_iterations_per_light = 20; data.diffuse_raster_steps = 0; data.diffuse_raster_size = 0.01; data.max_recursive_rays = 10; data.diffuse_rays_per_hit = 1; let mut scene_handler = scene::Scene::default(); //load_model::load_model(&mut data)?; let instance = create_instance(window, &entry, &mut data)?; data.surface = vk_window::create_surface(&instance, &window, &window)?; pick_physical_device(&instance, &mut data)?; let device = create_logical_device(&entry, &instance, &mut data)?; swapchain::create_swapchain(window, &instance, &device, &mut data)?; swapchain::create_swapchain_image_views(&device, &mut data)?; create_render_pass(&instance, &device, &mut data)?; buffer::create_descriptor_set_layout(&device, &mut data)?; create_pipeline(&device, &mut data)?; command_buffer::create_command_pool(&instance, &device, &mut data)?; create_color_objects(&instance, &device, &mut data)?; depth_buffer::create_depth_objects(&instance, &device, &mut data)?; create_framebuffers(&device, &mut data)?; image::create_texture_image(&instance, &device, &mut data)?; image::create_texture_image_view(&device, &mut data)?; image::create_texture_sampler(&device, &mut data)?; //let cur_pos = generators::generate_test_scene(&mut scene_handler, &mut data)?; let cur_pos = generators::generate_test_scene2(&mut scene_handler, &mut data, 31, 31,1, 5)?; scene_handler.prepare_data(&instance, &device, &mut data)?; buffer::create_uniform_buffers(&instance, &device, &mut data)?; buffer::create_storage_buffers(&instance, &device, &mut data)?; buffer::create_descriptor_pool(&device, &mut data)?; buffer::create_descriptor_sets(&device, &mut data)?; command_buffer::create_command_buffers(&device, &mut data, &scene_handler)?; create_sync_objects(&device, &mut data)?; Ok(Self { entry, instance, data, device, frame: 0 , resized: false, minimized: false, start: Instant::now(), cam_angle_x: 0.0, cam_angle_y: 0.0, last_pos: LogicalPosition::new(-1 as f32, -1 as f32), view_direction: vertex::Vec3::new(0.0, 0.0, 0.0), cur_pos: cur_pos, scene_handler, show_frame_rate: false, synchronized: 0 }) } /// Renders a frame for our Vulkan app. unsafe fn render(&mut self, window: &Window) -> Result<()> { let start_time = Instant::now(); let in_flight_fence = self.data.in_flight_fences[self.frame]; self.device.wait_for_fences(&[in_flight_fence], true, u64::MAX)?; let result = self.device.acquire_next_image_khr( self.data.swapchain, u64::MAX, self.data.image_available_semaphores[self.frame], vk::Fence::null(), ); let image_index = match result { Ok((image_index, _)) => image_index as usize, Err(vk::ErrorCode::OUT_OF_DATE_KHR) => return self.recreate_swapchain(window), Err(e) => return Err(anyhow!(e)), }; let image_in_flight = self.data.images_in_flight[image_index]; if !image_in_flight.is_null() { self.device.wait_for_fences(&[image_in_flight], true, u64::MAX)?; } self.data.images_in_flight[image_index] = in_flight_fence; self.update_uniform_buffer(image_index)?; if self.synchronized < MAX_FRAMES_IN_FLIGHT { buffer::update_storage_buffer(&self.instance, &self.device, &self.data, image_index, &self.scene_handler)?; self.synchronized += 1 } let wait_semaphores = &[self.data.image_available_semaphores[self.frame]]; let wait_stages = &[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; let command_buffers = &[self.data.command_buffers[image_index]]; let signal_semaphores = &[self.data.render_finished_semaphores[self.frame]]; let submit_info = vk::SubmitInfo::builder() .wait_semaphores(wait_semaphores) .wait_dst_stage_mask(wait_stages) .command_buffers(command_buffers) .signal_semaphores(signal_semaphores); self.device.reset_fences(&[in_flight_fence])?; self.device .queue_submit(self.data.graphics_queue, &[submit_info], in_flight_fence)?; let swapchains = &[self.data.swapchain]; let image_indices = &[image_index as u32]; let present_info = vk::PresentInfoKHR::builder() .wait_semaphores(signal_semaphores) .swapchains(swapchains) .image_indices(image_indices); let result = self.device.queue_present_khr(self.data.present_queue, &present_info); let changed = result == Ok(vk::SuccessCode::SUBOPTIMAL_KHR) || result == Err(vk::ErrorCode::OUT_OF_DATE_KHR); if self.resized || changed { self.resized = false; self.recreate_swapchain(window)?; } else if let Err(e) = result { return Err(anyhow!(e)); } self.frame = (self.frame + 1) % MAX_FRAMES_IN_FLIGHT; if self.show_frame_rate { println!("{}", 1000000.0 / start_time.elapsed().as_micros() as f32); } Ok(()) } /// Destroys our Vulkan app. unsafe fn destroy(&mut self) { self.destroy_swapchain(); self.device.destroy_sampler(self.data.texture_sampler, None); self.device.destroy_image_view(self.data.texture_image_view, None); self.device.destroy_image(self.data.texture_image, None); self.device.free_memory(self.data.texture_image_memory, None); self.device.destroy_descriptor_set_layout(self.data.descriptor_set_layout, None); self.scene_handler.destroy(&self.device); self.data.in_flight_fences .iter() .for_each(|f| self.device.destroy_fence(*f, None)); self.data.render_finished_semaphores .iter() .for_each(|s| self.device.destroy_semaphore(*s, None)); self.data.image_available_semaphores .iter() .for_each(|s| self.device.destroy_semaphore(*s, None)); self.device.destroy_command_pool(self.data.command_pool, None); self.device.destroy_device(None); self.instance.destroy_surface_khr(self.data.surface, None); if VALIDATION_ENABLED { self.instance.destroy_debug_utils_messenger_ext(self.data.messenger, None); } self.instance.destroy_instance(None); } unsafe fn recreate_swapchain(&mut self, window: &Window) -> Result<()> { self.device.device_wait_idle()?; self.destroy_swapchain(); swapchain::create_swapchain(window, &self.instance, &self.device, &mut self.data)?; swapchain::create_swapchain_image_views(&self.device, &mut self.data)?; create_render_pass(&self.instance, &self.device, &mut self.data)?; create_pipeline(&self.device, &mut self.data)?; buffer::create_descriptor_pool(&self.device, &mut self.data)?; create_color_objects(&self.instance, &self.device, &mut self.data)?; depth_buffer::create_depth_objects(&self.instance, &self.device, &mut self.data)?; create_framebuffers(&self.device, &mut self.data)?; buffer::create_uniform_buffers(&self.instance, &self.device, &mut self.data)?; buffer::create_storage_buffers(&self.instance, &self.device, &mut self.data)?; buffer::create_descriptor_sets(&self.device, &mut self.data)?; command_buffer::create_command_buffers(&self.device, &mut self.data, &self.scene_handler)?; self.data .images_in_flight .resize(self.data.swapchain_images.len(), vk::Fence::null()); self.synchronized = 0; Ok(()) } unsafe fn destroy_swapchain(&mut self) { self.device.destroy_image_view(self.data.color_image_view, None); self.device.free_memory(self.data.color_image_memory, None); self.device.destroy_image(self.data.color_image, None); self.device.destroy_image_view(self.data.depth_image_view, None); self.device.destroy_image(self.data.depth_image, None); self.device.free_memory(self.data.depth_image_memory, None); self.device.destroy_descriptor_pool(self.data.descriptor_pool, None); self.data.uniform_buffers .iter() .for_each(|b| self.device.destroy_buffer(*b, None)); self.data.uniform_buffers_memory .iter() .for_each(|m| self.device.free_memory(*m, None)); self.data.storage_buffers .iter() .for_each(|b| self.device.destroy_buffer(*b, None)); self.data.storage_buffers_memory .iter() .for_each(|m| self.device.free_memory(*m, None)); self.data.framebuffers .iter() .for_each(|f| self.device.destroy_framebuffer(*f, None)); self.device.free_command_buffers(self.data.command_pool, &self.data.command_buffers); self.device.destroy_pipeline(self.data.pipeline_cube, None); self.device.destroy_pipeline(self.data.pipeline_cuboid, None); self.device.destroy_pipeline(self.data.pipeline_quad, None); self.device.destroy_pipeline_layout(self.data.pipeline_layout, None); self.device.destroy_render_pass(self.data.render_pass, None); self.data.swapchain_image_views .iter() .for_each(|v| self.device.destroy_image_view(*v, None)); self.device.destroy_swapchain_khr(self.data.swapchain, None); } unsafe fn update_uniform_buffer(&mut self, image_index: usize) -> Result<()> { let time = self.start.elapsed().as_secs_f32(); /*let model = buffer::Mat4::from_axis_angle( vec3(0.0, 1.0, 0.0), cgmath::Deg(90.0) * 0.0 //time );*/ let rot_mat = cgmath::Matrix3::from_angle_y(cgmath::Deg(-self.cam_angle_y)) * cgmath::Matrix3::from_angle_z(cgmath::Deg(self.cam_angle_x)); let rot_mat4 = cgmath::Matrix4::from_angle_y(cgmath::Deg(-self.cam_angle_y)) * cgmath::Matrix4::from_angle_z(cgmath::Deg(self.cam_angle_x)); self.view_direction = rot_mat.transpose() * vertex::Vec3::new(1.0, 0.0, 0.0); let model = cgmath::Matrix4::from_translation( cgmath::Point3::new(0.0, 0.0, 0.0) - self.cur_pos ); let view = buffer::Mat4::look_to_rh( cgmath::point3(0.0, 0.0, 0.0), vertex::Vec3::new(1.0, 0.0, 0.0), vertex::Vec3::new(0.0, 0.0, 1.0) ); let correction = buffer::Mat4::new( //column major order, matrix looks transposed 1.0, 0.0, 0.0, 0.0, // We're also flipping the Y-axis with this line's `-1.0`. 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0 / 2.0, 0.0, 0.0, 0.0, 1.0 / 2.0, 1.0, ); let proj = correction * cgmath::perspective( cgmath::Deg(45.0), self.data.swapchain_extent.width as f32 / self.data.swapchain_extent.height as f32, 0.1, 10000.0, ); let ubo = buffer::UniformBufferObject { model, geom_rot: rot_mat4, view, proj, use_geom_shader: [self.data.use_geometry_shader, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false], camera_pos: self.cur_pos.clone()}; let memory = self.device.map_memory( self.data.uniform_buffers_memory[image_index], 0, size_of::<buffer::UniformBufferObject>() as u64, vk::MemoryMapFlags::empty(), )?; memcpy(&ubo, memory.cast(), 1); self.device.unmap_memory(self.data.uniform_buffers_memory[image_index]); Ok(()) } } //================================================ // MARK: Instance //================================================ unsafe fn create_instance(window: &Window, entry: &Entry, data: &mut app_data::AppData) -> Result<Instance> { let application_info = vk::ApplicationInfo::builder() .application_name(b"Vulkan Tutorial\0") .application_version(vk::make_version(1, 0, 0)) .engine_name(b"No Engine\0") .engine_version(vk::make_version(1, 0, 0)) .api_version(vk::make_version(1, 1, 0)); // Validation layers let available_layers = entry .enumerate_instance_layer_properties()? .iter() .map(|l| l.layer_name) .collect::<HashSet<_>>(); if VALIDATION_ENABLED && !available_layers.contains(&VALIDATION_LAYER) { return Err(anyhow!("Validation layer requested but not supported.")); } let layers = if VALIDATION_ENABLED { vec![VALIDATION_LAYER.as_ptr()] } else { Vec::new() }; // Extension let mut extensions = vk_window::get_required_instance_extensions(window) .iter() .map(|e| e.as_ptr()) .collect::<Vec<_>>(); if VALIDATION_ENABLED { extensions.push(vk::EXT_DEBUG_UTILS_EXTENSION.name.as_ptr()); } // Required by Vulkan SDK on macOS since 1.3.216. let flags = if cfg!(target_os = "macos") && entry.version()? >= PORTABILITY_MACOS_VERSION { info!("Enabling extensions for macOS portability."); extensions.push(vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_EXTENSION.name.as_ptr()); extensions.push(vk::KHR_PORTABILITY_ENUMERATION_EXTENSION.name.as_ptr()); vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR } else { vk::InstanceCreateFlags::empty() }; let mut info = vk::InstanceCreateInfo::builder() .application_info(&application_info) .enabled_layer_names(&layers) .enabled_extension_names(&extensions) .flags(flags); let mut debug_info = vk::DebugUtilsMessengerCreateInfoEXT::builder() .message_severity(vk::DebugUtilsMessageSeverityFlagsEXT::all()) .message_type(vk::DebugUtilsMessageTypeFlagsEXT::all()) .user_callback(Some(debug_callback)); if VALIDATION_ENABLED { info = info.push_next(&mut debug_info); } let instance = entry.create_instance(&info, None)?; if VALIDATION_ENABLED { data.messenger = instance.create_debug_utils_messenger_ext(&debug_info, None)?; } Ok(instance) } extern "system" fn debug_callback( severity: vk::DebugUtilsMessageSeverityFlagsEXT, type_: vk::DebugUtilsMessageTypeFlagsEXT, data: *const vk::DebugUtilsMessengerCallbackDataEXT, _: *mut c_void, ) -> vk::Bool32 { let data = unsafe { *data }; let message = unsafe { CStr::from_ptr(data.message) }.to_string_lossy(); if severity >= vk::DebugUtilsMessageSeverityFlagsEXT::ERROR { error!("({:?}) {}", type_, message); } else if severity >= vk::DebugUtilsMessageSeverityFlagsEXT::WARNING { warn!("({:?}) {}", type_, message); } else if severity >= vk::DebugUtilsMessageSeverityFlagsEXT::INFO { debug!("({:?}) {}", type_, message); } else { trace!("({:?}) {}", type_, message); } vk::FALSE } unsafe fn pick_physical_device(instance: &Instance, data: &mut app_data::AppData) -> Result<()> { for physical_device in instance.enumerate_physical_devices()? { let properties = instance.get_physical_device_properties(physical_device); if let Err(error) = check_physical_device(instance, data, physical_device) { warn!("Skipping physical device (`{}`): {}", properties.device_name, error); } else { info!("Selected physical device (`{}`).", properties.device_name); data.physical_device = physical_device; data.msaa_samples = get_max_msaa_samples(instance, data); return Ok(()); } } Err(anyhow!("Failed to find suitable physical device.")) } unsafe fn check_physical_device( instance: &Instance, data: &app_data::AppData, physical_device: vk::PhysicalDevice, ) -> Result<()> { let properties = instance.get_physical_device_properties(physical_device); if properties.device_type != vk::PhysicalDeviceType::DISCRETE_GPU { return Err(anyhow!(errors::SuitabilityError("Only discrete GPUs are supported."))); } let features = instance.get_physical_device_features(physical_device); if features.geometry_shader != vk::TRUE { return Err(anyhow!(errors::SuitabilityError("Missing geometry shader support."))); } if features.sampler_anisotropy != vk::TRUE { return Err(anyhow!(errors::SuitabilityError("No sampler anisotropy."))); } queue_family_indices::QueueFamilyIndices::get(instance, data, physical_device)?; check_physical_device_extensions(instance, physical_device)?; let support = swapchain::SwapchainSupport::get(instance, data, physical_device)?; if support.formats.is_empty() || support.present_modes.is_empty() { return Err(anyhow!(errors::SuitabilityError("Insufficient swapchain support."))); } Ok(()) } unsafe fn check_physical_device_extensions( instance: &Instance, physical_device: vk::PhysicalDevice, ) -> Result<()> { let extensions = instance .enumerate_device_extension_properties(physical_device, None)? .iter() .map(|e| e.extension_name) .collect::<HashSet<_>>(); if DEVICE_EXTENSIONS.iter().all(|e| extensions.contains(e)) { Ok(()) } else { let missing = DEVICE_EXTENSIONS.iter().filter(|e| !extensions.contains(e)).collect::<Vec<&vk::ExtensionName>>(); for missing_extension in missing { println!("Missing extension: {}", missing_extension); } Err(anyhow!(errors::SuitabilityError("Missing required device extensions."))) } } unsafe fn create_logical_device( entry: &Entry, instance: &Instance, data: &mut app_data::AppData, ) -> Result<Device> { let indices = queue_family_indices::QueueFamilyIndices::get(instance, data, data.physical_device)?; let queue_priorities = &[1.0]; let queue_info = vk::DeviceQueueCreateInfo::builder() .queue_family_index(indices.graphics) .queue_priorities(queue_priorities); let layers = if VALIDATION_ENABLED { vec![VALIDATION_LAYER.as_ptr()] } else { vec![] }; let mut extensions = DEVICE_EXTENSIONS .iter() .map(|n| n.as_ptr()) .collect::<Vec<_>>(); // Required by Vulkan SDK on macOS since 1.3.216. if cfg!(target_os = "macos") && entry.version()? >= PORTABILITY_MACOS_VERSION { extensions.push(vk::KHR_PORTABILITY_SUBSET_EXTENSION.name.as_ptr()); }; let features = vk::PhysicalDeviceFeatures::builder() .sampler_anisotropy(true) .geometry_shader(true); let indices = queue_family_indices::QueueFamilyIndices::get(instance, data, data.physical_device)?; let mut unique_indices = HashSet::new(); unique_indices.insert(indices.graphics); unique_indices.insert(indices.present); let queue_priorities = &[1.0]; let queue_infos = unique_indices .iter() .map(|i| { vk::DeviceQueueCreateInfo::builder() .queue_family_index(*i) .queue_priorities(queue_priorities) }) .collect::<Vec<_>>(); let info = vk::DeviceCreateInfo::builder() .queue_create_infos(&queue_infos) .enabled_layer_names(&layers) .enabled_extension_names(&extensions) .enabled_features(&features); let device = instance.create_device(data.physical_device, &info, None)?; data.graphics_queue = device.get_device_queue(indices.graphics, 0); data.present_queue = device.get_device_queue(indices.present, 0); Ok(device) } unsafe fn create_pipeline(device: &Device, data: &mut app_data::AppData) -> Result<()> { // set up shaders for cubes // load the byte data let vert_cube = include_bytes!("../shaders/compiled/vert_cube.spv"); let geo_cube = include_bytes!("../shaders/compiled/geo_cube.spv"); let frag_cube = include_bytes!("../shaders/compiled/frag_cube.spv"); // create the shaders let vert_shader_module_cube = create_shader_module(device, &vert_cube[..])?; let geo_shader_module_cube = create_shader_module(device, &geo_cube[..])?; let frag_shader_module_cube = create_shader_module(device, &frag_cube[..])?; //create the shader stage for the vertex shader let vert_stage_cube = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::VERTEX) .module(vert_shader_module_cube) .name(b"main\0"); //create the shader stage for the geometry shader let geo_stage_cube = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::GEOMETRY) .module(geo_shader_module_cube) .name(b"main\0"); //create the shader stage for the fragment shader let frag_stage_cube = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::FRAGMENT) .module(frag_shader_module_cube) .name(b"main\0"); // create the binding description for the cube vertex let binding_descriptions_cube = &[vertex::Vertex::binding_description()]; let attribute_descriptions_cube = vertex::Vertex::attribute_descriptions(); let vertex_input_state_cube = vk::PipelineVertexInputStateCreateInfo::builder() .vertex_binding_descriptions(binding_descriptions_cube) .vertex_attribute_descriptions(&attribute_descriptions_cube); // set up shaders for cuboids // load the byte data let vert_cuboid = include_bytes!("../shaders/compiled/vert_cuboid.spv"); let geo_cuboid = include_bytes!("../shaders/compiled/geo_cuboid.spv"); let frag_cuboid = include_bytes!("../shaders/compiled/frag_cuboid.spv"); // create the shaders let vert_shader_module_cuboid = create_shader_module(device, &vert_cuboid[..])?; let geo_shader_module_cuboid = create_shader_module(device, &geo_cuboid[..])?; let frag_shader_module_cuboid = create_shader_module(device, &frag_cuboid[..])?; //create the shader stage for the vertex shader let vert_stage_cuboid = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::VERTEX) .module(vert_shader_module_cuboid) .name(b"main\0"); //create the shader stage for the geometry shader let geo_stage_cuboid = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::GEOMETRY) .module(geo_shader_module_cuboid) .name(b"main\0"); //create the shader stage for the fragment shader let frag_stage_cuboid = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::FRAGMENT) .module(frag_shader_module_cuboid) .name(b"main\0"); // create the binding description for the sized vertex let binding_descriptions_cuboid = &[vertex::SizedVertex::binding_description()]; let attribute_descriptions_cuboid = vertex::SizedVertex::attribute_descriptions(); let vertex_input_state_cuboid = vk::PipelineVertexInputStateCreateInfo::builder() .vertex_binding_descriptions(binding_descriptions_cuboid) .vertex_attribute_descriptions(&attribute_descriptions_cuboid); // set up shaders for quads/raytracing // load the byte data let vert_quad = include_bytes!("../shaders/compiled/vert_rt_quad.spv"); let frag_quad = include_bytes!("../shaders/compiled/frag_rt_quad.spv"); // create the shaders let vert_shader_module_quad = create_shader_module(device, &vert_quad[..])?; let frag_shader_module_quad = create_shader_module(device, &frag_quad[..])?; //create the shader stage for the vertex shader let vert_stage_quad = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::VERTEX) .module(vert_shader_module_quad) .name(b"main\0"); //create the shader stage for the fragment shader let frag_stage_quad = vk::PipelineShaderStageCreateInfo::builder() .stage(vk::ShaderStageFlags::FRAGMENT) .module(frag_shader_module_quad) .name(b"main\0"); // create the binding description for the quad vertex let binding_descriptions_quad = &[vertex::RTVertex::binding_description()]; let attribute_descriptions_quad = vertex::RTVertex::attribute_descriptions(); let vertex_input_state_quad = vk::PipelineVertexInputStateCreateInfo::builder() .vertex_binding_descriptions(binding_descriptions_quad) .vertex_attribute_descriptions(&attribute_descriptions_quad); // define input assembly and object type. This is altered when using geometry shader let mut topology = vk::PrimitiveTopology::TRIANGLE_LIST; if data.use_geometry_shader { topology = vk::PrimitiveTopology::POINT_LIST; } data.topology = topology; let input_assembly_state = vk::PipelineInputAssemblyStateCreateInfo::builder() .topology(topology) .primitive_restart_enable(false); // define viewport and other transformations when projecting onto the screen let viewport = vk::Viewport::builder() .x(0.0) .y(0.0) .width(data.swapchain_extent.width as f32) .height(data.swapchain_extent.height as f32) .min_depth(0.0) .max_depth(1.0); let scissor = vk::Rect2D::builder() .offset(vk::Offset2D { x: 0, y: 0 }) .extent(data.swapchain_extent); let viewports = &[viewport]; let scissors = &[scissor]; let viewport_state = vk::PipelineViewportStateCreateInfo::builder() .viewports(viewports) .scissors(scissors); let rasterization_state = vk::PipelineRasterizationStateCreateInfo::builder() .depth_clamp_enable(false) .rasterizer_discard_enable(false) .polygon_mode(vk::PolygonMode::FILL) .line_width(1.0) .cull_mode(vk::CullModeFlags::BACK) .front_face(vk::FrontFace::COUNTER_CLOCKWISE) .depth_bias_enable(false); let multisample_state = vk::PipelineMultisampleStateCreateInfo::builder() .sample_shading_enable(false) .rasterization_samples(data.msaa_samples); let depth_stencil_state = vk::PipelineDepthStencilStateCreateInfo::builder() .depth_test_enable(true) .depth_write_enable(true) .depth_compare_op(vk::CompareOp::LESS) .depth_bounds_test_enable(false) .min_depth_bounds(0.0) // Optional. .max_depth_bounds(1.0) // Optional. .stencil_test_enable(false); let attachment = vk::PipelineColorBlendAttachmentState::builder() .color_write_mask(vk::ColorComponentFlags::all()) .blend_enable(false) .src_color_blend_factor(vk::BlendFactor::ONE) // Optional .dst_color_blend_factor(vk::BlendFactor::ZERO) // Optional .color_blend_op(vk::BlendOp::ADD) // Optional .src_alpha_blend_factor(vk::BlendFactor::ONE) // Optional .dst_alpha_blend_factor(vk::BlendFactor::ZERO) // Optional .alpha_blend_op(vk::BlendOp::ADD); // Optional let attachments = &[attachment]; let color_blend_state = vk::PipelineColorBlendStateCreateInfo::builder() .logic_op_enable(false) .logic_op(vk::LogicOp::COPY) .attachments(attachments) .blend_constants([0.0, 0.0, 0.0, 0.0]); // define the work pipeline let set_layouts = &[data.descriptor_set_layout]; let layout_info = vk::PipelineLayoutCreateInfo::builder() .set_layouts(set_layouts); data.pipeline_layout = device.create_pipeline_layout(&layout_info, None)?; // define stages for the cubes pipeline let stages_cube = &[vert_stage_cube, frag_stage_cube]; let stages_geom_cube = &[vert_stage_cube, geo_stage_cube,frag_stage_cube]; let mut info_cube = vk::GraphicsPipelineCreateInfo::builder() .vertex_input_state(&vertex_input_state_cube) .input_assembly_state(&input_assembly_state) .viewport_state(&viewport_state) .rasterization_state(&rasterization_state) .multisample_state(&multisample_state) .depth_stencil_state(&depth_stencil_state) .color_blend_state(&color_blend_state) .layout(data.pipeline_layout) .render_pass(data.render_pass) .subpass(0); if data.use_geometry_shader { info_cube = info_cube.stages(stages_geom_cube); } else { info_cube = info_cube.stages(stages_cube); } // define stages for the cuboid pipeline let stages_cuboid = &[vert_stage_cuboid, frag_stage_cuboid]; let stages_geom_cuboid = &[vert_stage_cuboid, geo_stage_cuboid,frag_stage_cuboid]; let mut info_cuboid = vk::GraphicsPipelineCreateInfo::builder() .vertex_input_state(&vertex_input_state_cuboid) .input_assembly_state(&input_assembly_state) .viewport_state(&viewport_state) .rasterization_state(&rasterization_state) .multisample_state(&multisample_state) .depth_stencil_state(&depth_stencil_state) .color_blend_state(&color_blend_state) .layout(data.pipeline_layout) .render_pass(data.render_pass) .subpass(0); if data.use_geometry_shader { info_cuboid = info_cuboid.stages(stages_geom_cuboid); } else { info_cuboid = info_cuboid.stages(stages_cuboid); } // define stages for the quad/rt pipeline let stages_quad = &[vert_stage_quad, frag_stage_quad]; let mut info_quad = vk::GraphicsPipelineCreateInfo::builder() .vertex_input_state(&vertex_input_state_quad) .input_assembly_state(&input_assembly_state) .viewport_state(&viewport_state) .rasterization_state(&rasterization_state) .multisample_state(&multisample_state) .depth_stencil_state(&depth_stencil_state) .color_blend_state(&color_blend_state) .layout(data.pipeline_layout) .render_pass(data.render_pass) .subpass(0); info_quad = info_quad.stages(stages_quad); // create the pipeline let pipelines = device.create_graphics_pipelines(vk::PipelineCache::null(), &[info_cube, info_cuboid, info_quad], None)?.0; data.pipeline_cube = pipelines[0]; data.pipeline_cuboid = pipelines[1]; data.pipeline_quad = pipelines[2]; device.destroy_shader_module(vert_shader_module_cube, None); device.destroy_shader_module(geo_shader_module_cube, None); device.destroy_shader_module(frag_shader_module_cube, None); device.destroy_shader_module(vert_shader_module_cuboid, None); device.destroy_shader_module(geo_shader_module_cuboid, None); device.destroy_shader_module(frag_shader_module_cuboid, None); device.destroy_shader_module(vert_shader_module_quad, None); device.destroy_shader_module(frag_shader_module_quad, None); Ok(()) } unsafe fn create_shader_module( device: &Device, bytecode: &[u8], ) -> Result<vk::ShaderModule> { let bytecode = Bytecode::new(bytecode).unwrap(); let info = vk::ShaderModuleCreateInfo::builder() .code_size(bytecode.code_size()) .code(bytecode.code()); Ok(device.create_shader_module(&info, None)?) } unsafe fn create_render_pass( instance: &Instance, device: &Device, data: &mut app_data::AppData, ) -> Result<()> { let color_attachment = vk::AttachmentDescription::builder() .format(data.swapchain_format) .samples(data.msaa_samples) .load_op(vk::AttachmentLoadOp::CLEAR) .store_op(vk::AttachmentStoreOp::STORE) .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) .initial_layout(vk::ImageLayout::UNDEFINED) .final_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); let color_attachment_ref = vk::AttachmentReference::builder() .attachment(0) .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); let color_resolve_attachment = vk::AttachmentDescription::builder() .format(data.swapchain_format) .samples(vk::SampleCountFlags::_1) .load_op(vk::AttachmentLoadOp::DONT_CARE) .store_op(vk::AttachmentStoreOp::STORE) .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) .initial_layout(vk::ImageLayout::UNDEFINED) .final_layout(vk::ImageLayout::PRESENT_SRC_KHR); let color_resolve_attachment_ref = vk::AttachmentReference::builder() .attachment(2) .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); let depth_stencil_attachment = vk::AttachmentDescription::builder() .format(depth_buffer::get_depth_format(instance, data)?) .samples(data.msaa_samples) .load_op(vk::AttachmentLoadOp::CLEAR) .store_op(vk::AttachmentStoreOp::DONT_CARE) .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) .initial_layout(vk::ImageLayout::UNDEFINED) .final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL); let depth_stencil_attachment_ref = vk::AttachmentReference::builder() .attachment(1) .layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL); let color_attachments = &[color_attachment_ref]; let resolve_attachments = &[color_resolve_attachment_ref]; let subpass = vk::SubpassDescription::builder() .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) .color_attachments(color_attachments) .depth_stencil_attachment(&depth_stencil_attachment_ref) .resolve_attachments(resolve_attachments); let dependency = vk::SubpassDependency::builder() .src_subpass(vk::SUBPASS_EXTERNAL) .dst_subpass(0) .src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS) .src_access_mask(vk::AccessFlags::empty()) .dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS) .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE); let attachments = &[color_attachment, depth_stencil_attachment, color_resolve_attachment]; let subpasses = &[subpass]; let dependencies = &[dependency]; let info = vk::RenderPassCreateInfo::builder() .attachments(attachments) .subpasses(subpasses) .dependencies(dependencies); data.render_pass = device.create_render_pass(&info, None)?; Ok(()) } unsafe fn create_framebuffers(device: &Device, data: &mut app_data::AppData) -> Result<()> { data.framebuffers = data .swapchain_image_views .iter() .map(|i| { let attachments = &[data.color_image_view, data.depth_image_view, *i]; let create_info = vk::FramebufferCreateInfo::builder() .render_pass(data.render_pass) .attachments(attachments) .width(data.swapchain_extent.width) .height(data.swapchain_extent.height) .layers(1); device.create_framebuffer(&create_info, None) }) .collect::<Result<Vec<_>, _>>()?; Ok(()) } unsafe fn create_sync_objects(device: &Device, data: &mut app_data::AppData) -> Result<()> { let semaphore_info = vk::SemaphoreCreateInfo::builder(); let fence_info = vk::FenceCreateInfo::builder() .flags(vk::FenceCreateFlags::SIGNALED); for _ in 0..MAX_FRAMES_IN_FLIGHT { data.image_available_semaphores .push(device.create_semaphore(&semaphore_info, None)?); data.render_finished_semaphores .push(device.create_semaphore(&semaphore_info, None)?); data.in_flight_fences.push(device.create_fence(&fence_info, None)?); } data.images_in_flight = data.swapchain_images .iter() .map(|_| vk::Fence::null()) .collect(); Ok(()) } unsafe fn get_max_msaa_samples( instance: &Instance, data: &app_data::AppData, ) -> vk::SampleCountFlags { let properties = instance.get_physical_device_properties(data.physical_device); let counts = properties.limits.framebuffer_color_sample_counts & properties.limits.framebuffer_depth_sample_counts; [ vk::SampleCountFlags::_64, vk::SampleCountFlags::_32, vk::SampleCountFlags::_16, vk::SampleCountFlags::_8, vk::SampleCountFlags::_4, vk::SampleCountFlags::_2, ] .iter() .cloned() .find(|c| counts.contains(*c)) .unwrap_or(vk::SampleCountFlags::_1) } unsafe fn create_color_objects( instance: &Instance, device: &Device, data: &mut app_data::AppData, ) -> Result<()> { let (color_image, color_image_memory) = image::create_image( instance, device, data, data.swapchain_extent.width, data.swapchain_extent.height, 1, data.msaa_samples, data.swapchain_format, vk::ImageTiling::OPTIMAL, vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSIENT_ATTACHMENT, vk::MemoryPropertyFlags::DEVICE_LOCAL, )?; data.color_image = color_image; data.color_image_memory = color_image_memory; data.color_image_view = image::create_image_view( device, data.color_image, data.swapchain_format, vk::ImageAspectFlags::COLOR, 1, )?; Ok(()) }