This commit is contained in:
2020-11-07 17:54:30 +01:00
parent 38ea41b550
commit a8f8bbf36e
5 changed files with 88 additions and 33 deletions

1
config/graphics.toml Normal file
View File

@@ -0,0 +1 @@
msaa_samples = 8

View File

@@ -1,3 +1,3 @@
input_events = false input_events = false
vulkan_validation_layers = false vulkan_validation_layers = true
mesh_load_info = true mesh_load_info = true

View File

@@ -14,3 +14,14 @@ impl LogConfig {
toml::from_slice(&fs::read(path).expect("Failed to read log config!")).expect("Failed to parse log config!") 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!")
}
}

View File

@@ -1,7 +1,7 @@
use cgmath::{Matrix4, vec3, Vector3, Vector4}; use cgmath::{Matrix4, vec3, Vector3, Vector4};
use winit::event::Event; use winit::event::Event;
use crate::config::LogConfig; use crate::config::{LogConfig, RenderConfig};
use crate::gameobject::{GameObject, GameObjectHandle, Updatable}; use crate::gameobject::{GameObject, GameObjectHandle, Updatable};
use crate::input::InputState; use crate::input::InputState;
use crate::player::Player; use crate::player::Player;
@@ -171,15 +171,18 @@ fn main() {
let mut game = TestGame::new("config/input.toml", log_config); let mut game = TestGame::new("config/input.toml", log_config);
let line_count = 30; 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( let (mut renderer, event_loop) = VulkanRenderer::init(
(-line_count..=line_count) line_vertices,
.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(),
log_config.vulkan_validation_layers, log_config.vulkan_validation_layers,
RenderConfig::from_file("config/graphics.toml")
); );
game.game_start(&mut renderer); game.game_start(&mut renderer);

View File

@@ -11,7 +11,7 @@ use vulkano::descriptor::DescriptorSet;
use vulkano::device::{Device, DeviceExtensions, Queue}; use vulkano::device::{Device, DeviceExtensions, Queue};
use vulkano::format::{ClearValue, Format}; use vulkano::format::{ClearValue, Format};
use vulkano::framebuffer::{Framebuffer, FramebufferAbstract, RenderPassAbstract, Subpass}; 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::{ApplicationInfo, Instance, InstanceExtensions, PhysicalDevice, Version};
use vulkano::instance::debug::{DebugCallback, MessageSeverity, MessageType}; use vulkano::instance::debug::{DebugCallback, MessageSeverity, MessageType};
use vulkano::memory::pool::{PotentialDedicatedAllocation, StdMemoryPoolAlloc}; use vulkano::memory::pool::{PotentialDedicatedAllocation, StdMemoryPoolAlloc};
@@ -32,8 +32,10 @@ use vs::ty::PushConstants;
use crate::gameobject::{GameObject, GameObjectHandle}; use crate::gameobject::{GameObject, GameObjectHandle};
use crate::mesh::CPUMesh; use crate::mesh::CPUMesh;
use crate::config::RenderConfig;
const VALIDATION_LAYERS: &[&str] = &[ const VALIDATION_LAYERS: &[&str] = &[
"VK_LAYER_KHRONOS_validation"
]; ];
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone)]
@@ -107,10 +109,12 @@ pub struct VulkanRenderer {
pub debug_callback: Option<DebugCallback>, pub debug_callback: Option<DebugCallback>,
pub previous_frame_end: Option<Box<dyn GpuFuture>>, pub previous_frame_end: Option<Box<dyn GpuFuture>>,
pub uniform_buffers: Vec<Arc<CpuAccessibleBuffer<vs::ty::ObjectUniformData>>>, pub uniform_buffers: Vec<Arc<CpuAccessibleBuffer<vs::ty::ObjectUniformData>>>,
pub msaa_sample_count: u32,
} }
impl VulkanRenderer { impl VulkanRenderer {
pub fn init(line_vertices: Vec<LinePoint>, enable_validation_layers: bool) -> (VulkanRenderer, EventLoop<()>) { pub fn init(line_vertices: Vec<LinePoint>, enable_validation_layers: bool, render_config: RenderConfig) -> (VulkanRenderer, EventLoop<()>) {
// Create empty game data struct to be filled
let mut data = GameData { let mut data = GameData {
push_constants: PushConstants { push_constants: PushConstants {
model: Matrix4::identity().into(), model: Matrix4::identity().into(),
@@ -131,10 +135,7 @@ impl VulkanRenderer {
use_line_pipeline: true, use_line_pipeline: true,
}; };
if enable_validation_layers { // Create basic vulkan instance with layers and info
println!("Enabling validation layers...");
}
let instance = { let instance = {
let extensions = InstanceExtensions { let extensions = InstanceExtensions {
ext_debug_utils: true, ext_debug_utils: true,
@@ -149,11 +150,13 @@ impl VulkanRenderer {
}; };
if enable_validation_layers { if enable_validation_layers {
println!("Enabling validation layers...");
let available_layers = vulkano::instance::layers_list().unwrap().map(|layer| String::from(layer.name())).collect::<Vec<String>>(); let available_layers = vulkano::instance::layers_list().unwrap().map(|layer| String::from(layer.name())).collect::<Vec<String>>();
println!("Available layers: {:?}", available_layers);
VALIDATION_LAYERS.iter().for_each(|wanted_layer_name| { VALIDATION_LAYERS.iter().for_each(|wanted_layer_name| {
if !available_layers.iter().any(|available_layer_name| available_layer_name == 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! // lifetime of this is important, even tho it isn't used!
let mut debug_callback = None; let mut debug_callback = None;
// Debug stuff
if enable_validation_layers { if enable_validation_layers {
let msg_severity = MessageSeverity { let msg_severity = MessageSeverity {
verbose: false, verbose: false,
@@ -193,6 +198,7 @@ impl VulkanRenderer {
}).ok(); }).ok();
} }
// TODO: Just get the first physical device we find, it's fiiiine...
let physical = PhysicalDevice::enumerate(&instance).next().unwrap(); let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
println!("Using device: {} (type: {:?})", physical.name(), physical.ty()); 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 surface = WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap();
let window = surface.window(); 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 // 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. // queue to handle data transfers in parallel. In this example we only use one queue.
let queue_family = physical.queue_families().find(|&q| { let queue_family = physical.queue_families().find(|&q| {
q.supports_graphics() && surface.is_supported(q).unwrap_or(false) q.supports_graphics() && surface.is_supported(q).unwrap_or(false)
}).unwrap(); }).unwrap();
// Queue
let device_ext = DeviceExtensions { khr_swapchain: true, ..DeviceExtensions::none() }; let device_ext = DeviceExtensions { khr_swapchain: true, ..DeviceExtensions::none() };
let (device, mut queues) = Device::new(physical, physical.supported_features(), &device_ext, let (device, mut queues) = Device::new(physical, physical.supported_features(), &device_ext,
[(queue_family, 0.5)].iter().cloned()).unwrap(); [(queue_family, 0.5)].iter().cloned()).unwrap();
let queue = queues.next().unwrap(); let queue = queues.next().unwrap();
// Swapchain
let (swapchain, images) = { let (swapchain, images) = {
let caps = surface.capabilities(physical).unwrap(); let caps = surface.capabilities(physical).unwrap();
let usage = caps.supported_usage_flags; let usage = caps.supported_usage_flags;
@@ -227,27 +236,35 @@ impl VulkanRenderer {
// what is host_cached // what is host_cached
let line_vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::vertex_buffer(), false, data.line_vertices.iter().cloned()).unwrap(); 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!( let render_pass = Arc::new(vulkano::single_pass_renderpass!(
device.clone(), device.clone(),
attachments: { attachments: {
color: { color: {
load: Clear, load: DontCare,
store: Store, store: Store,
format: swapchain.format(), format: swapchain.format(),
samples: 1, samples: 1,
}, },
depth: { intermediary: {
load: Clear, load: Clear,
store: DontCare, store: DontCare,
format: swapchain.format(),
samples: render_config.msaa_samples,
},
depth: {
load: Clear,
store: Store,
format: Format::D16Unorm, format: Format::D16Unorm,
samples: 1, samples: render_config.msaa_samples,
initial_layout: ImageLayout::Undefined, initial_layout: ImageLayout::Undefined,
final_layout: ImageLayout::DepthStencilAttachmentOptimal, final_layout: ImageLayout::DepthStencilAttachmentOptimal,
} }
}, },
pass: { pass: {
color: [color], color: [intermediary],
depth_stencil: {depth} depth_stencil: {depth},
resolve: [color]
} }
).unwrap()); ).unwrap());
@@ -281,9 +298,11 @@ impl VulkanRenderer {
// Otherwise we would have to recreate the whole pipeline. // 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 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 // 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. // 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 mut uniform_buffers = Vec::new();
let uniform_buffer = vs::ty::ObjectUniformData { let uniform_buffer = vs::ty::ObjectUniformData {
@@ -320,14 +339,16 @@ impl VulkanRenderer {
(VulkanRenderer { game_data: data, device, framebuffers, sampler, (VulkanRenderer { game_data: data, device, framebuffers, sampler,
dynamic_state, pipeline, line_pipeline, uniform_buffers, dynamic_state, pipeline, line_pipeline, uniform_buffers,
surface, swapchain, render_pass, queue, line_vertex_buffer, 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<AutoCommandBuffer> { fn create_command_buffer(self: &mut Self, fb_index: usize, uniform_buffer_data: vs::ty::ObjectUniformData) -> Arc<AutoCommandBuffer> {
// General setup // General setup
let mut builder = AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), self.queue.family()).unwrap(); 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.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. // Load and draw meshes etc.
for i in 0..self.game_data.game_objects.len() { for i in 0..self.game_data.game_objects.len() {
@@ -354,6 +375,14 @@ impl VulkanRenderer {
Arc::new(builder.build().unwrap()) Arc::new(builder.build().unwrap())
} }
fn create_msaa_buffers(device: Arc<Device>, dimensions: [u32; 2], swapchain: &Arc<Swapchain<Window>>, sample_count: u32) -> Vec<Arc<AttachmentImage>> {
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) { pub fn render_loop(self: &mut Self, new_ubo: vs::ty::ObjectUniformData) {
// cleanup previous frame // cleanup previous frame
self.previous_frame_end.as_mut().unwrap().cleanup_finished(); self.previous_frame_end.as_mut().unwrap().cleanup_finished();
@@ -375,10 +404,12 @@ impl VulkanRenderer {
Err(err) => panic!("{:?}", err), 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; self.swapchain = new_swapchain;
// Because framebuffers contains an Arc on the old swapchain, we need to // Because framebuffers contains an Arc on the old swapchain, we need to
// recreate framebuffers as well. // 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; self.recreate_swapchain = false;
} }
@@ -507,25 +538,34 @@ pub fn start_event_loop(mut renderer: VulkanRenderer, mut game: Box<dyn Game>, e
} }
/// This method is called once during initialization, then again whenever the window is resized /// This method is called once during initialization, then again whenever the window is resized
fn window_size_dependent_setup(device: Arc<Device>, images: &[Arc<SwapchainImage<Window>>], render_pass: Arc<dyn RenderPassAbstract + Send + Sync>, dynamic_state: &mut DynamicState) -> Vec<Arc<dyn FramebufferAbstract + Send + Sync>> { fn window_size_dependent_setup(device: Arc<Device>, images: &[Arc<SwapchainImage<Window>>], msaa_buffers: &[Arc<AttachmentImage>], msaa_sample_count: u32, render_pass: Arc<dyn RenderPassAbstract + Send + Sync>, dynamic_state: &mut DynamicState) -> Vec<Arc<dyn FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions(); 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 { let viewport = Viewport {
origin: [0.0, 0.0], origin: [0.0, 0.0],
dimensions: [dimensions[0] as f32, dimensions[1] as f32], dimensions: dim_array,
depth_range: 0.0 .. 1.0, depth_range: 0.0 .. 1.0,
}; };
dynamic_state.viewports = Some(vec!(viewport)); 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| { let mut framebuffers = vec![];
Arc::new(Framebuffer::start(render_pass.clone()) for i in 0..images.len() {
.add(image.clone()).unwrap() 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() .add(depth_image.clone()).unwrap()
.build().unwrap() .build().unwrap()
) as Arc<dyn FramebufferAbstract + Send + Sync> ) as Arc<dyn FramebufferAbstract + Send + Sync>);
}).collect::<Vec<_>>() }
framebuffers
} }
pub mod vs { pub mod vs {