diff --git a/config/graphics.toml b/config/graphics.toml new file mode 100644 index 0000000..ce24e2b --- /dev/null +++ b/config/graphics.toml @@ -0,0 +1 @@ +msaa_samples = 8 \ No newline at end of file diff --git a/config/log.toml b/config/log.toml index 4719d10..21f4239 100644 --- a/config/log.toml +++ b/config/log.toml @@ -1,3 +1,3 @@ input_events = false -vulkan_validation_layers = false +vulkan_validation_layers = true mesh_load_info = true \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index c5db96f..aa42ae0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,4 +13,15 @@ impl LogConfig { pub fn from_file(path: &str) -> Self { toml::from_slice(&fs::read(path).expect("Failed to read log config!")).expect("Failed to parse log config!") } +} + +#[derive(Debug, Serialize, Deserialize, Clone, Copy)] +pub struct RenderConfig { + pub msaa_samples: u32 +} + +impl RenderConfig { + pub fn from_file(path: &str) -> Self { + toml::from_slice(&fs::read(path).expect("Failed to read render config!")).expect("Failed to parse render config!") + } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 74b4b64..598d3d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use cgmath::{Matrix4, vec3, Vector3, Vector4}; use winit::event::Event; -use crate::config::LogConfig; +use crate::config::{LogConfig, RenderConfig}; use crate::gameobject::{GameObject, GameObjectHandle, Updatable}; use crate::input::InputState; use crate::player::Player; @@ -171,15 +171,18 @@ fn main() { let mut game = TestGame::new("config/input.toml", log_config); let line_count = 30; + let line_vertices = (-line_count..=line_count) + .flat_map(|it| vec![ + LinePoint { position: [it as f32, 0., -line_count as f32] }, + LinePoint { position: [it as f32, 0., line_count as f32] }, + LinePoint { position: [-line_count as f32, 0., it as f32] }, + LinePoint { position: [line_count as f32, 0., it as f32] }, + ]).collect(); + let (mut renderer, event_loop) = VulkanRenderer::init( - (-line_count..=line_count) - .flat_map(|it| vec![ - LinePoint { position: [it as f32, 0., -line_count as f32] }, - LinePoint { position: [it as f32, 0., line_count as f32] }, - LinePoint { position: [-line_count as f32, 0., it as f32] }, - LinePoint { position: [line_count as f32, 0., it as f32] }, - ]).collect(), + line_vertices, log_config.vulkan_validation_layers, + RenderConfig::from_file("config/graphics.toml") ); game.game_start(&mut renderer); diff --git a/src/vulkan.rs b/src/vulkan.rs index 4ea6993..6bd5f98 100644 --- a/src/vulkan.rs +++ b/src/vulkan.rs @@ -11,7 +11,7 @@ use vulkano::descriptor::DescriptorSet; use vulkano::device::{Device, DeviceExtensions, Queue}; use vulkano::format::{ClearValue, Format}; use vulkano::framebuffer::{Framebuffer, FramebufferAbstract, RenderPassAbstract, Subpass}; -use vulkano::image::{AttachmentImage, Dimensions, ImageUsage, ImmutableImage, SwapchainImage}; +use vulkano::image::{AttachmentImage, Dimensions, ImageUsage, ImmutableImage, SwapchainImage, ImageViewAccess}; use vulkano::instance::{ApplicationInfo, Instance, InstanceExtensions, PhysicalDevice, Version}; use vulkano::instance::debug::{DebugCallback, MessageSeverity, MessageType}; use vulkano::memory::pool::{PotentialDedicatedAllocation, StdMemoryPoolAlloc}; @@ -32,8 +32,10 @@ use vs::ty::PushConstants; use crate::gameobject::{GameObject, GameObjectHandle}; use crate::mesh::CPUMesh; +use crate::config::RenderConfig; const VALIDATION_LAYERS: &[&str] = &[ + "VK_LAYER_KHRONOS_validation" ]; #[derive(Default, Debug, Clone)] @@ -107,10 +109,12 @@ pub struct VulkanRenderer { pub debug_callback: Option, pub previous_frame_end: Option>, pub uniform_buffers: Vec>>, + pub msaa_sample_count: u32, } impl VulkanRenderer { - pub fn init(line_vertices: Vec, enable_validation_layers: bool) -> (VulkanRenderer, EventLoop<()>) { + pub fn init(line_vertices: Vec, enable_validation_layers: bool, render_config: RenderConfig) -> (VulkanRenderer, EventLoop<()>) { + // Create empty game data struct to be filled let mut data = GameData { push_constants: PushConstants { model: Matrix4::identity().into(), @@ -131,10 +135,7 @@ impl VulkanRenderer { use_line_pipeline: true, }; - if enable_validation_layers { - println!("Enabling validation layers..."); - } - + // Create basic vulkan instance with layers and info let instance = { let extensions = InstanceExtensions { ext_debug_utils: true, @@ -149,11 +150,13 @@ impl VulkanRenderer { }; if enable_validation_layers { + println!("Enabling validation layers..."); let available_layers = vulkano::instance::layers_list().unwrap().map(|layer| String::from(layer.name())).collect::>(); + println!("Available layers: {:?}", available_layers); VALIDATION_LAYERS.iter().for_each(|wanted_layer_name| { if !available_layers.iter().any(|available_layer_name| available_layer_name == wanted_layer_name) { - panic!("Validation layer not found: {:?}. Available layers: {:?}", wanted_layer_name, &available_layers.join(", ")); + panic!("Validation layer not found: {:?}", wanted_layer_name); } }); @@ -165,6 +168,8 @@ impl VulkanRenderer { // lifetime of this is important, even tho it isn't used! let mut debug_callback = None; + + // Debug stuff if enable_validation_layers { let msg_severity = MessageSeverity { verbose: false, @@ -193,6 +198,7 @@ impl VulkanRenderer { }).ok(); } + // TODO: Just get the first physical device we find, it's fiiiine... let physical = PhysicalDevice::enumerate(&instance).next().unwrap(); println!("Using device: {} (type: {:?})", physical.name(), physical.ty()); @@ -200,17 +206,20 @@ impl VulkanRenderer { let surface = WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap(); let window = surface.window(); + // TODO: Tutorial says we need more queues // In a real-life application, we would probably use at least a graphics queue and a transfers // queue to handle data transfers in parallel. In this example we only use one queue. let queue_family = physical.queue_families().find(|&q| { q.supports_graphics() && surface.is_supported(q).unwrap_or(false) }).unwrap(); + // Queue let device_ext = DeviceExtensions { khr_swapchain: true, ..DeviceExtensions::none() }; let (device, mut queues) = Device::new(physical, physical.supported_features(), &device_ext, [(queue_family, 0.5)].iter().cloned()).unwrap(); let queue = queues.next().unwrap(); + // Swapchain let (swapchain, images) = { let caps = surface.capabilities(physical).unwrap(); let usage = caps.supported_usage_flags; @@ -227,27 +236,35 @@ impl VulkanRenderer { // what is host_cached let line_vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::vertex_buffer(), false, data.line_vertices.iter().cloned()).unwrap(); + // Render pass let render_pass = Arc::new(vulkano::single_pass_renderpass!( device.clone(), attachments: { color: { - load: Clear, + load: DontCare, store: Store, format: swapchain.format(), samples: 1, }, - depth: { + intermediary: { load: Clear, store: DontCare, + format: swapchain.format(), + samples: render_config.msaa_samples, + }, + depth: { + load: Clear, + store: Store, format: Format::D16Unorm, - samples: 1, + samples: render_config.msaa_samples, initial_layout: ImageLayout::Undefined, final_layout: ImageLayout::DepthStencilAttachmentOptimal, } }, pass: { - color: [color], - depth_stencil: {depth} + color: [intermediary], + depth_stencil: {depth}, + resolve: [color] } ).unwrap()); @@ -281,9 +298,11 @@ impl VulkanRenderer { // Otherwise we would have to recreate the whole pipeline. let mut dynamic_state = DynamicState { line_width: None, viewports: None, scissors: None, compare_mask: None, write_mask: None, reference: None }; + let msaa_attachments = Self::create_msaa_buffers(device.clone(), data.dimensions, &swapchain, render_config.msaa_samples); + // The render pass we created above only describes the layout of our framebuffers. Before we // can draw we also need to create the actual framebuffers. - let framebuffers = window_size_dependent_setup(device.clone(), &images, render_pass.clone(), &mut dynamic_state); + let framebuffers = window_size_dependent_setup(device.clone(), &images, &msaa_attachments, render_config.msaa_samples, render_pass.clone(), &mut dynamic_state); let mut uniform_buffers = Vec::new(); let uniform_buffer = vs::ty::ObjectUniformData { @@ -320,14 +339,16 @@ impl VulkanRenderer { (VulkanRenderer { game_data: data, device, framebuffers, sampler, dynamic_state, pipeline, line_pipeline, uniform_buffers, surface, swapchain, render_pass, queue, line_vertex_buffer, - recreate_swapchain: false, debug_callback, previous_frame_end }, events_loop) + recreate_swapchain: false, debug_callback, previous_frame_end, + msaa_sample_count: render_config.msaa_samples + }, events_loop) } fn create_command_buffer(self: &mut Self, fb_index: usize, uniform_buffer_data: vs::ty::ObjectUniformData) -> Arc { // General setup let mut builder = AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), self.queue.family()).unwrap(); builder.update_buffer(self.uniform_buffers[fb_index].clone(), uniform_buffer_data).unwrap(); - builder.begin_render_pass(self.framebuffers[fb_index].clone(), false, vec![ClearValue::Float([0.0, 0.0, 0.0, 1.0]), ClearValue::Depth(1.0)]).unwrap(); + builder.begin_render_pass(self.framebuffers[fb_index].clone(), false, vec![ClearValue::None, ClearValue::Float([0.0, 0.0, 0.0, 1.0]), ClearValue::Depth(1.0)]).unwrap(); // Load and draw meshes etc. for i in 0..self.game_data.game_objects.len() { @@ -354,6 +375,14 @@ impl VulkanRenderer { Arc::new(builder.build().unwrap()) } + fn create_msaa_buffers(device: Arc, dimensions: [u32; 2], swapchain: &Arc>, sample_count: u32) -> Vec> { + let mut msaa_attachments = vec![]; + for _ in 0..swapchain.num_images() { + msaa_attachments.push(AttachmentImage::transient_multisampled(device.clone(), dimensions, sample_count, swapchain.format()).unwrap()); + } + msaa_attachments + } + pub fn render_loop(self: &mut Self, new_ubo: vs::ty::ObjectUniformData) { // cleanup previous frame self.previous_frame_end.as_mut().unwrap().cleanup_finished(); @@ -375,10 +404,12 @@ impl VulkanRenderer { Err(err) => panic!("{:?}", err), }; + let msaa_buffers = Self::create_msaa_buffers(self.device.clone(), self.game_data.dimensions, &new_swapchain, self.msaa_sample_count); + self.swapchain = new_swapchain; // Because framebuffers contains an Arc on the old swapchain, we need to // recreate framebuffers as well. - self.framebuffers = window_size_dependent_setup(self.device.clone(), &new_images, self.render_pass.clone(), &mut self.dynamic_state); + self.framebuffers = window_size_dependent_setup(self.device.clone(), &new_images, &msaa_buffers, self.msaa_sample_count, self.render_pass.clone(), &mut self.dynamic_state); self.recreate_swapchain = false; } @@ -507,25 +538,34 @@ pub fn start_event_loop(mut renderer: VulkanRenderer, mut game: Box, e } /// This method is called once during initialization, then again whenever the window is resized -fn window_size_dependent_setup(device: Arc, images: &[Arc>], render_pass: Arc, dynamic_state: &mut DynamicState) -> Vec> { +fn window_size_dependent_setup(device: Arc, images: &[Arc>], msaa_buffers: &[Arc], msaa_sample_count: u32, render_pass: Arc, dynamic_state: &mut DynamicState) -> Vec> { let dimensions = images[0].dimensions(); + let dim_array = [dimensions.width() as f32, dimensions.height() as f32]; + let dim_array_u32 = [dimensions.width() as u32, dimensions.height() as u32]; let viewport = Viewport { origin: [0.0, 0.0], - dimensions: [dimensions[0] as f32, dimensions[1] as f32], + dimensions: dim_array, depth_range: 0.0 .. 1.0, }; dynamic_state.viewports = Some(vec!(viewport)); - let depth_image = AttachmentImage::with_usage(device.clone(), dimensions, Format::D16Unorm, ImageUsage { depth_stencil_attachment: true, ..ImageUsage::none() }).unwrap(); + let depth_image = AttachmentImage::multisampled_with_usage(device.clone(), dim_array_u32, msaa_sample_count, Format::D16Unorm, ImageUsage { depth_stencil_attachment: true, ..ImageUsage::none() }).unwrap(); - images.iter().map(|image| { - Arc::new(Framebuffer::start(render_pass.clone()) - .add(image.clone()).unwrap() + let mut framebuffers = vec![]; + for i in 0..images.len() { + let image_buffer = &images[i]; + let msaa_buffer = &msaa_buffers[i]; + + framebuffers.push(Arc::new(Framebuffer::start(render_pass.clone()) + .add(image_buffer.clone()).unwrap() + .add(msaa_buffer.clone()).unwrap() .add(depth_image.clone()).unwrap() .build().unwrap() - ) as Arc - }).collect::>() + ) as Arc); + } + + framebuffers } pub mod vs {