refactor 2
This commit is contained in:
12
src/main.rs
12
src/main.rs
@@ -2,7 +2,7 @@ use winit::{Event};
|
|||||||
use cgmath::{Matrix4, Rad, Vector3, Deg, Quaternion, Rotation3, One, Rotation, SquareMatrix};
|
use cgmath::{Matrix4, Rad, Vector3, Deg, Quaternion, Rotation3, One, Rotation, SquareMatrix};
|
||||||
|
|
||||||
mod vulkan;
|
mod vulkan;
|
||||||
use crate::vulkan::{GameData, Game, LinePoint, GameObject, VulkanRenderer};
|
use crate::vulkan::{GameData, Game, LinePoint, GameObject, VulkanRenderer, RenderLoopResult};
|
||||||
|
|
||||||
mod input;
|
mod input;
|
||||||
use crate::input::{InputState};
|
use crate::input::{InputState};
|
||||||
@@ -100,5 +100,13 @@ fn main() {
|
|||||||
log_config.vulkan_validation_layers,
|
log_config.vulkan_validation_layers,
|
||||||
);
|
);
|
||||||
renderer.upload_mesh(mesh::load_mesh("models/cube.dae", true).into_iter().nth(0).unwrap());
|
renderer.upload_mesh(mesh::load_mesh("models/cube.dae", true).into_iter().nth(0).unwrap());
|
||||||
renderer.render_loop(&mut game);
|
|
||||||
|
let mut continue_rendering = true;
|
||||||
|
|
||||||
|
while continue_rendering {
|
||||||
|
match renderer.render_loop(&mut game) {
|
||||||
|
RenderLoopResult::Ok => {},
|
||||||
|
RenderLoopResult::Quit => continue_rendering = false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
255
src/vulkan.rs
255
src/vulkan.rs
@@ -34,6 +34,7 @@ use vs::ty::PushConstants;
|
|||||||
use line_vs::ty::LinePushConstants;
|
use line_vs::ty::LinePushConstants;
|
||||||
|
|
||||||
use crate::mesh::CPUMesh;
|
use crate::mesh::CPUMesh;
|
||||||
|
use crate::vulkan::RenderLoopResult::Quit;
|
||||||
|
|
||||||
const VALIDATION_LAYERS: &[&str] = &[
|
const VALIDATION_LAYERS: &[&str] = &[
|
||||||
"VK_LAYER_LUNARG_standard_validation"
|
"VK_LAYER_LUNARG_standard_validation"
|
||||||
@@ -97,7 +98,14 @@ pub struct VulkanRenderer {
|
|||||||
pub render_pass: Arc<RenderPassAbstract + Send + Sync>,
|
pub render_pass: Arc<RenderPassAbstract + Send + Sync>,
|
||||||
pub queue: Arc<Queue>,
|
pub queue: Arc<Queue>,
|
||||||
pub events_loop: EventsLoop,
|
pub events_loop: EventsLoop,
|
||||||
|
pub recreate_swapchain: bool,
|
||||||
pub debug_callback: Option<DebugCallback>,
|
pub debug_callback: Option<DebugCallback>,
|
||||||
|
pub previous_frame_end: Option<Box<GpuFuture>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum RenderLoopResult {
|
||||||
|
Ok,
|
||||||
|
Quit
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VulkanRenderer {
|
impl VulkanRenderer {
|
||||||
@@ -273,145 +281,146 @@ impl VulkanRenderer {
|
|||||||
// 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, render_pass.clone(), &mut dynamic_state);
|
||||||
|
|
||||||
VulkanRenderer { game_data: data, device, framebuffers, dynamic_state, pipeline, line_pipeline,
|
|
||||||
surface, swapchain, render_pass, queue, line_vertex_buffer, events_loop, debug_callback }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_loop(self: &mut Self, game: &mut dyn Game) {
|
|
||||||
let mut recreate_swapchain = false;
|
|
||||||
|
|
||||||
// In the loop below we are going to submit commands to the GPU. Submitting a command produces
|
// In the loop below we are going to submit commands to the GPU. Submitting a command produces
|
||||||
// an object that implements the `GpuFuture` trait, which holds the resources for as long as
|
// an object that implements the `GpuFuture` trait, which holds the resources for as long as
|
||||||
// they are in use by the GPU.
|
// they are in use by the GPU.
|
||||||
//
|
//
|
||||||
// Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to avoid
|
// Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to avoid
|
||||||
// that, we store the submission of the previous frame here.
|
// that, we store the submission of the previous frame here.
|
||||||
let mut previous_frame_end = Box::new(sync::now(self.device.clone())) as Box<dyn GpuFuture>;
|
let previous_frame_end = Some(Box::new(sync::now(device.clone())) as Box<dyn GpuFuture>);
|
||||||
|
|
||||||
loop {
|
VulkanRenderer { game_data: data, device, framebuffers, dynamic_state, pipeline, line_pipeline,
|
||||||
// It is important to call this function from time to time, otherwise resources will keep
|
surface, swapchain, render_pass, queue, line_vertex_buffer, events_loop,
|
||||||
// accumulating and you will eventually reach an out of memory error.
|
recreate_swapchain: false, debug_callback, previous_frame_end }
|
||||||
// Calling this function polls various fences in order to determine what the GPU has
|
}
|
||||||
// already processed, and frees the resources that are no longer needed.
|
|
||||||
previous_frame_end.cleanup_finished();
|
|
||||||
|
|
||||||
if recreate_swapchain {
|
pub fn render_loop(self: &mut Self, game: &mut dyn Game) -> RenderLoopResult {
|
||||||
let window = self.surface.window();
|
// It is important to call this function from time to time, otherwise resources will keep
|
||||||
self.game_data.dimensions = if let Some(dimensions) = window.get_inner_size() {
|
// accumulating and you will eventually reach an out of memory error.
|
||||||
let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
|
// Calling this function polls various fences in order to determine what the GPU has
|
||||||
[dimensions.0, dimensions.1]
|
// already processed, and frees the resources that are no longer needed.
|
||||||
} else {
|
self.previous_frame_end.as_mut().unwrap().cleanup_finished();
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let (new_swapchain, new_images) = match self.swapchain.recreate_with_dimension(self.game_data.dimensions) {
|
if self.recreate_swapchain {
|
||||||
Ok(r) => r,
|
let window = self.surface.window();
|
||||||
// This error tends to happen when the user is manually resizing the window.
|
self.game_data.dimensions = if let Some(dimensions) = window.get_inner_size() {
|
||||||
// Simply restarting the loop is the easiest way to fix this issue.
|
let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
|
||||||
Err(SwapchainCreationError::UnsupportedDimensions) => continue,
|
[dimensions.0, dimensions.1]
|
||||||
Err(err) => panic!("{:?}", err)
|
} else {
|
||||||
};
|
return RenderLoopResult::Ok;
|
||||||
|
};
|
||||||
|
|
||||||
self.swapchain = new_swapchain;
|
let (new_swapchain, new_images) = match self.swapchain.recreate_with_dimension(self.game_data.dimensions) {
|
||||||
// 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);
|
|
||||||
|
|
||||||
recreate_swapchain = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.game_data.recreate_pipeline {
|
|
||||||
if let Some(pipeline_ok) = create_pipeline::<Vertex>(self.device.clone(), self.render_pass.clone(), "shaders/triangle.vert", "shaders/triangle.frag", false) {
|
|
||||||
self.pipeline = pipeline_ok;
|
|
||||||
println!("Updated pipeline.");
|
|
||||||
} else {
|
|
||||||
println!("Failed to update pipeline.");
|
|
||||||
}
|
|
||||||
self.game_data.recreate_pipeline = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Before we can draw on the output, we have to *acquire* an image from the swapchain. If
|
|
||||||
// no image is available (which happens if you submit draw commands too quickly), then the
|
|
||||||
// function will block.
|
|
||||||
// This operation returns the index of the image that we are allowed to draw upon.
|
|
||||||
//
|
|
||||||
// This function can block if no image is available. The parameter is an optional timeout
|
|
||||||
// after which the function call will return an error.
|
|
||||||
let (image_num, acquire_future) = match swapchain::acquire_next_image(self.swapchain.clone(), None) {
|
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(AcquireError::OutOfDate) => {
|
// This error tends to happen when the user is manually resizing the window.
|
||||||
recreate_swapchain = true;
|
// Simply restarting the loop is the easiest way to fix this issue.
|
||||||
continue;
|
Err(SwapchainCreationError::UnsupportedDimensions) => return RenderLoopResult::Ok,
|
||||||
},
|
|
||||||
Err(err) => panic!("{:?}", err)
|
Err(err) => panic!("{:?}", err)
|
||||||
};
|
};
|
||||||
|
|
||||||
game.update(&mut self.game_data);
|
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);
|
||||||
|
|
||||||
let mut cbb = AutoCommandBufferBuilder::primary_one_time_submit(self.device.clone(), self.queue.family()).unwrap()
|
self.recreate_swapchain = false;
|
||||||
// Before we can draw, we have to *enter a render pass*. There are two methods to do
|
|
||||||
// this: `draw_inline` and `draw_secondary`. The latter is a bit more advanced and is
|
|
||||||
// not covered here.
|
|
||||||
.begin_render_pass(self.framebuffers[image_num].clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into(), ClearValue::Depth(1.0)]).unwrap();
|
|
||||||
|
|
||||||
|
|
||||||
// We are now inside the first subpass of the render pass
|
|
||||||
for i in 0..self.game_data.game_objects.len() {
|
|
||||||
let game_object = &self.game_data.game_objects[i];
|
|
||||||
let mesh = &self.game_data.meshes[game_object.mesh_index];
|
|
||||||
self.game_data.push_constants.model = game_object.model_matrix.into();
|
|
||||||
cbb = cbb.draw_indexed(self.pipeline.clone(), &self.dynamic_state,
|
|
||||||
vec![mesh.vertex_buffer.clone()],
|
|
||||||
mesh.index_buffer.clone(),
|
|
||||||
(), self.game_data.push_constants.clone()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
cbb = cbb.draw(self.line_pipeline.clone(), &self.dynamic_state, vec![self.line_vertex_buffer.clone()], (), self.game_data.line_push_constants.clone()).unwrap()
|
|
||||||
|
|
||||||
// We leave the render pass by calling `draw_end`. Note that if we had multiple
|
|
||||||
// subpasses we could have called `next_inline` (or `next_secondary`) to jump to the
|
|
||||||
// next subpass.
|
|
||||||
.end_render_pass().unwrap();
|
|
||||||
|
|
||||||
let command_buffer = cbb.build().unwrap();
|
|
||||||
|
|
||||||
let future = previous_frame_end.join(acquire_future)
|
|
||||||
.then_execute(self.queue.clone(), command_buffer).unwrap()
|
|
||||||
.then_swapchain_present(self.queue.clone(), self.swapchain.clone(), image_num)
|
|
||||||
.then_signal_fence_and_flush();
|
|
||||||
|
|
||||||
match future {
|
|
||||||
Ok(future) => {
|
|
||||||
previous_frame_end = Box::new(future) as Box<_>;
|
|
||||||
}
|
|
||||||
Err(FlushError::OutOfDate) => {
|
|
||||||
recreate_swapchain = true;
|
|
||||||
previous_frame_end = Box::new(sync::now(self.device.clone())) as Box<_>;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("{:?}", e);
|
|
||||||
previous_frame_end = Box::new(sync::now(self.device.clone())) as Box<_>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that in more complex programs it is likely that one of `acquire_next_image`,
|
|
||||||
// `command_buffer::submit`, or `present` will block for some time. This happens when the
|
|
||||||
// GPU's queue is full and the driver has to wait until the GPU finished some work.
|
|
||||||
//
|
|
||||||
// Unfortunately the Vulkan API doesn't provide any way to not wait or to detect when a
|
|
||||||
// wait would happen. Blocking may be the desired behavior, but if you don't want to
|
|
||||||
// block you should spawn a separate thread dedicated to submissions.
|
|
||||||
let mut window_closed = false;
|
|
||||||
self.events_loop.poll_events(|ev| {
|
|
||||||
game.on_window_event(&ev);
|
|
||||||
match ev {
|
|
||||||
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => window_closed = true,
|
|
||||||
Event::WindowEvent { event: WindowEvent::Resized(_), .. } => recreate_swapchain = true,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if self.game_data.shutdown || window_closed { return; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.game_data.recreate_pipeline {
|
||||||
|
if let Some(pipeline_ok) = create_pipeline::<Vertex>(self.device.clone(), self.render_pass.clone(), "shaders/triangle.vert", "shaders/triangle.frag", false) {
|
||||||
|
self.pipeline = pipeline_ok;
|
||||||
|
println!("Updated pipeline.");
|
||||||
|
} else {
|
||||||
|
println!("Failed to update pipeline.");
|
||||||
|
}
|
||||||
|
self.game_data.recreate_pipeline = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before we can draw on the output, we have to *acquire* an image from the swapchain. If
|
||||||
|
// no image is available (which happens if you submit draw commands too quickly), then the
|
||||||
|
// function will block.
|
||||||
|
// This operation returns the index of the image that we are allowed to draw upon.
|
||||||
|
//
|
||||||
|
// This function can block if no image is available. The parameter is an optional timeout
|
||||||
|
// after which the function call will return an error.
|
||||||
|
let (image_num, acquire_future) = match swapchain::acquire_next_image(self.swapchain.clone(), None) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(AcquireError::OutOfDate) => {
|
||||||
|
self.recreate_swapchain = true;
|
||||||
|
return RenderLoopResult::Ok;
|
||||||
|
},
|
||||||
|
Err(err) => panic!("{:?}", err)
|
||||||
|
};
|
||||||
|
|
||||||
|
game.update(&mut self.game_data);
|
||||||
|
|
||||||
|
let mut cbb = AutoCommandBufferBuilder::primary_one_time_submit(self.device.clone(), self.queue.family()).unwrap()
|
||||||
|
// Before we can draw, we have to *enter a render pass*. There are two methods to do
|
||||||
|
// this: `draw_inline` and `draw_secondary`. The latter is a bit more advanced and is
|
||||||
|
// not covered here.
|
||||||
|
.begin_render_pass(self.framebuffers[image_num].clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into(), ClearValue::Depth(1.0)]).unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
// We are now inside the first subpass of the render pass
|
||||||
|
for i in 0..self.game_data.game_objects.len() {
|
||||||
|
let game_object = &self.game_data.game_objects[i];
|
||||||
|
let mesh = &self.game_data.meshes[game_object.mesh_index];
|
||||||
|
self.game_data.push_constants.model = game_object.model_matrix.into();
|
||||||
|
cbb = cbb.draw_indexed(self.pipeline.clone(), &self.dynamic_state,
|
||||||
|
vec![mesh.vertex_buffer.clone()],
|
||||||
|
mesh.index_buffer.clone(),
|
||||||
|
(), self.game_data.push_constants.clone()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
cbb = cbb.draw(self.line_pipeline.clone(), &self.dynamic_state, vec![self.line_vertex_buffer.clone()], (), self.game_data.line_push_constants.clone()).unwrap()
|
||||||
|
|
||||||
|
// We leave the render pass by calling `draw_end`. Note that if we had multiple
|
||||||
|
// subpasses we could have called `next_inline` (or `next_secondary`) to jump to the
|
||||||
|
// next subpass.
|
||||||
|
.end_render_pass().unwrap();
|
||||||
|
|
||||||
|
let command_buffer = cbb.build().unwrap();
|
||||||
|
|
||||||
|
let future = self.previous_frame_end.take().unwrap().join(acquire_future)
|
||||||
|
.then_execute(self.queue.clone(), command_buffer).unwrap()
|
||||||
|
.then_swapchain_present(self.queue.clone(), self.swapchain.clone(), image_num)
|
||||||
|
.then_signal_fence_and_flush();
|
||||||
|
|
||||||
|
match future {
|
||||||
|
Ok(future) => {
|
||||||
|
self.previous_frame_end = Some(Box::new(future) as Box<_>);
|
||||||
|
},
|
||||||
|
Err(FlushError::OutOfDate) => {
|
||||||
|
self.recreate_swapchain = true;
|
||||||
|
self.previous_frame_end = Some(Box::new(sync::now(self.device.clone())) as Box<_>);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("{:?}", e);
|
||||||
|
self.previous_frame_end = Some(Box::new(sync::now(self.device.clone())) as Box<_>);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note that in more complex programs it is likely that one of `acquire_next_image`,
|
||||||
|
// `command_buffer::submit`, or `present` will block for some time. This happens when the
|
||||||
|
// GPU's queue is full and the driver has to wait until the GPU finished some work.
|
||||||
|
//
|
||||||
|
// Unfortunately the Vulkan API doesn't provide any way to not wait or to detect when a
|
||||||
|
// wait would happen. Blocking may be the desired behavior, but if you don't want to
|
||||||
|
// block you should spawn a separate thread dedicated to submissions.
|
||||||
|
let mut window_closed = false;
|
||||||
|
let mut resized = false;
|
||||||
|
self.events_loop.poll_events(|ev| {
|
||||||
|
game.on_window_event(&ev);
|
||||||
|
match ev {
|
||||||
|
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => window_closed = true,
|
||||||
|
Event::WindowEvent { event: WindowEvent::Resized(_), .. } => resized = true,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if resized { self.recreate_swapchain = true }
|
||||||
|
if self.game_data.shutdown || window_closed { return Quit; }
|
||||||
|
|
||||||
|
RenderLoopResult::Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upload_mesh(self: &mut Self, mesh: CPUMesh) -> usize {
|
pub fn upload_mesh(self: &mut Self, mesh: CPUMesh) -> usize {
|
||||||
|
|||||||
Reference in New Issue
Block a user