restructure

This commit is contained in:
2019-07-26 22:37:02 +02:00
parent 8c14653cfc
commit 3bb9c40749
2 changed files with 85 additions and 125 deletions

View File

@@ -1,17 +1,42 @@
use crate::vulkan::{Vertex, GameData};
use crate::vulkan::{Vertex, GameData, Game};
use winit::{Event, WindowEvent, ElementState};
use std::time::SystemTime;
use std::iter::FromIterator;
use cgmath::{Matrix4, SquareMatrix, Rad, Point3, Vector3, Deg};
use cgmath::{Matrix4, Rad, Point3, Vector3, Deg};
mod vulkan;
use vulkan::vs::ty::PushConstants;
const PRINT_KEYBOARD_INPUT: bool = false;
impl GameData<'_> {
/// Returns true if event should be ignored by the vulkan handler
fn on_window_event(self: &mut Self, event: &Event) -> bool {
struct TestGame {}
impl Game for TestGame {
fn update(self: &mut Self, game_data: &mut GameData) {
game_data.push_constants.time = game_data.start_time.elapsed().unwrap().as_millis() as f32 / 1000.0;
let model = Matrix4::from_angle_z(Rad::from(Deg(game_data.push_constants.time * 100.0)));
let view = Matrix4::look_at(
Point3::new(2.0, 2.0, 2.0),
Point3::new(0.0, 0.0, 0.0),
Vector3::new(0.0, 0.0, 1.0)
);
let mut proj = cgmath::perspective(
Rad::from(Deg(45.0)),
game_data.aspect_ratio,
0.1,
10.0
);
proj.y.y *= -1.0;
game_data.push_constants.model = model.into();
game_data.push_constants.view = view.into();
game_data.push_constants.projection = proj.into();
}
fn on_window_event(self: &mut Self, game_data: &mut GameData, event: &Event) -> bool {
match event {
Event::WindowEvent { event: WindowEvent::KeyboardInput { device_id, input }, .. } => {
if PRINT_KEYBOARD_INPUT {
@@ -28,71 +53,29 @@ impl GameData<'_> {
}
if input.state == ElementState::Released && input.modifiers.ctrl && input.scancode == 19 {
self.recreate_pipeline = true;
game_data.recreate_pipeline = true;
}
if input.state == ElementState::Released && input.scancode == 1 {
self.shutdown = true;
game_data.shutdown = true;
}
}
_ => {}
}
return false;
}
fn update_push_constants(self: &mut Self) {
self.push_constants.time = self.start_time.elapsed().unwrap().as_millis() as f32 / 1000.0;
let model = Matrix4::from_angle_z(Rad::from(Deg(self.push_constants.time * 100.0)));
let view = Matrix4::look_at(
Point3::new(2.0, 2.0, 2.0),
Point3::new(0.0, 0.0, 0.0),
Vector3::new(0.0, 0.0, 1.0)
);
let mut proj = cgmath::perspective(
Rad::from(Deg(45.0)),
self.aspect_ratio,
0.1,
10.0
);
proj.y.y *= -1.0;
self.push_constants.model = model.into();
self.push_constants.view = view.into();
self.push_constants.projection = proj.into();
}
}
fn main() {
// let mut whatever = String::new();
// std::io::stdin().read_line(&mut whatever).unwrap();
let mut pc = PushConstants {
time: 0.0,
_dummy0: [0; 12],
model: Matrix4::identity().into(),
view: Matrix4::identity().into(),
projection: Matrix4::identity().into(),
};
let data = GameData {
mesh_vertices: vec![
Vertex { position: [0.1, 0.2, 0.2] },
Vertex { position: [0.2, 0.4, 0.2] },
Vertex { position: [0.2, 0.2, 0.3] }
],
line_vertices: vec![
Vertex { position: [-0.9, 1., 0.] },
Vertex { position: [0.9, 0., 0.] },
],
push_constants: &mut pc,
start_time: SystemTime::now(),
recreate_pipeline: false,
aspect_ratio: 1.0,
shutdown: false,
};
vulkan::init(data);
let mut game = TestGame {};
vulkan::init(vec![
Vertex { position: [0.1, 0.2, 0.2] },
Vertex { position: [0.2, 0.4, 0.2] },
Vertex { position: [0.2, 0.2, 0.3] }
],
vec![
Vertex { position: [-0.9, 1., 0.] },
Vertex { position: [0.9, 0., 0.] },
],
&mut game
);
}

View File

@@ -24,6 +24,8 @@ use std::time::SystemTime;
use std::path::PathBuf;
use std::ffi::{CStr};
use cgmath::{Matrix4, SquareMatrix};
use shade_runner;
use shade_runner::{CompiledShaders, Entry};
@@ -46,17 +48,40 @@ pub struct Vertex {
}
vulkano::impl_vertex!(Vertex, position);
pub struct GameData<'a> {
pub trait Game {
fn update(self: &mut Self, game_data: &mut GameData);
/// Returns true if event should be ignored by the vulkan handler
fn on_window_event(self: &mut Self, game_data: &mut GameData, event: &Event) -> bool;
}
pub struct GameData {
pub start_time: SystemTime,
pub mesh_vertices: Vec<Vertex>,
pub line_vertices: Vec<Vertex>,
pub push_constants: &'a mut PushConstants,
pub push_constants: PushConstants,
pub recreate_pipeline: bool,
pub aspect_ratio: f32,
pub shutdown: bool,
}
pub fn init(mut game: GameData) {
pub fn init(mesh_vertices: Vec<Vertex>, line_vertices: Vec<Vertex>, game: &mut dyn Game) {
let mut data = GameData {
push_constants: PushConstants {
time: 0.0,
_dummy0: [0; 12],
model: Matrix4::identity().into(),
view: Matrix4::identity().into(),
projection: Matrix4::identity().into(),
},
start_time: SystemTime::now(),
recreate_pipeline: false,
aspect_ratio: 1.0,
shutdown: false,
mesh_vertices,
line_vertices,
};
if ENABLE_VALIDATION_LAYERS {
println!("Enabling validation layers...");
}
@@ -122,16 +147,8 @@ pub fn init(mut game: GameData) {
let surface = WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap();
let window = surface.window();
// The next step is to choose which GPU queue will execute our draw commands.
//
// Devices can provide multiple queues to run commands in parallel (for example a draw queue
// and a compute queue), similar to CPU threads. This is something you have to have to manage
// manually in Vulkan.
//
// 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.
//
// We have to choose which queues to use early on, because we will need this info very soon.
let queue_family = physical.queue_families().find(|&q| {
q.supports_graphics() && surface.is_supported(q).unwrap_or(false)
}).unwrap();
@@ -164,22 +181,19 @@ pub fn init(mut game: GameData) {
//
// Because for both of these cases, the swapchain needs to be the window dimensions, we just use that.
let initial_dimensions = if let Some(dimensions) = window.get_inner_size() {
// convert to physical pixels
let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
[dimensions.0, dimensions.1]
} else {
// The window no longer exists so exit the application.
panic!("idk");
panic!("Couldn't get window dimensions!");
};
// Please take a look at the docs for the meaning of the parameters we didn't mention.
Swapchain::new(device.clone(), surface.clone(), caps.min_image_count, format,
initial_dimensions, 1, usage, &queue, SurfaceTransform::Identity, alpha,
PresentMode::Fifo, true, None).unwrap()
};
let mesh_vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), game.mesh_vertices.iter().cloned()).unwrap();
let line_vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), game.line_vertices.iter().cloned()).unwrap();
let mesh_vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), data.mesh_vertices.iter().cloned()).unwrap();
let line_vertex_buffer = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), data.line_vertices.iter().cloned()).unwrap();
let render_pass = Arc::new(vulkano::single_pass_renderpass!(
device.clone(),
@@ -214,20 +228,8 @@ pub fn init(mut game: GameData) {
// 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.
//
// Since we need to draw to multiple images, we are going to create a different framebuffer for
// each image.
let mut framebuffers = window_size_dependent_setup(device.clone(), &images, render_pass.clone(), &mut dynamic_state, &mut game.aspect_ratio);
let mut framebuffers = window_size_dependent_setup(device.clone(), &images, render_pass.clone(), &mut dynamic_state, &mut data.aspect_ratio);
// In some situations, the swapchain will become invalid by it This includes for example
// when the window is resized (as the images of the swapchain will no longer match the
// window's) or, on Android, when the application went to the background and goes back to the
// foreground.
//
// In this situation, acquiring a swapchain image or presenting it will return an error.
// Rendering to an image of that swapchain will not produce any error, but may or may not work.
// To continue rendering, we need to recreate the swapchain by creating a new swapchain.
// Here, we remember that we need to do this for the next loop iteration.
let mut recreate_swapchain = false;
// In the loop below we are going to submit commands to the GPU. Submitting a command produces
@@ -245,10 +247,7 @@ pub fn init(mut game: GameData) {
// already processed, and frees the resources that are no longer needed.
previous_frame_end.cleanup_finished();
// Whenever the window resizes we need to recreate everything dependent on the window size.
// In this example that includes the swapchain, the framebuffers and the dynamic state viewport.
if recreate_swapchain {
// Get the new dimensions of the window.
let dimensions = if let Some(dimensions) = window.get_inner_size() {
let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
[dimensions.0, dimensions.1]
@@ -267,19 +266,19 @@ pub fn init(mut game: GameData) {
swapchain = new_swapchain;
// Because framebuffers contains an Arc on the old swapchain, we need to
// recreate framebuffers as well.
framebuffers = window_size_dependent_setup(device.clone(), &new_images, render_pass.clone(), &mut dynamic_state, &mut game.aspect_ratio);
framebuffers = window_size_dependent_setup(device.clone(), &new_images, render_pass.clone(), &mut dynamic_state, &mut data.aspect_ratio);
recreate_swapchain = false;
}
if game.recreate_pipeline {
if data.recreate_pipeline {
if let Some(pipeline_ok) = create_pipeline(device.clone(), sub_pass.clone(), "shaders/triangle.vert", "shaders/triangle.frag", false) {
pipeline = pipeline_ok;
println!("Updated pipeline.");
} else {
println!("Failed to update pipeline.");
}
game.recreate_pipeline = false;
data.recreate_pipeline = false;
}
// Before we can draw on the output, we have to *acquire* an image from the swapchain. If
@@ -298,20 +297,11 @@ pub fn init(mut game: GameData) {
Err(err) => panic!("{:?}", err)
};
game.update_push_constants();
game.update(&mut data);
// Specify the color to clear the framebuffer with i.e. blue
let clear_values = vec!([0.0, 0.0, 1.0, 1.0].into(), 1f32.into());
// In order to draw, we have to build a *command buffer*. The command buffer object holds
// the list of commands that are going to be executed.
//
// Building a command buffer is an expensive operation (usually a few hundred
// microseconds), but it is known to be a hot path in the driver and is expected to be
// optimized.
//
// Note that we have to pass a queue family when we create the command buffer. The command
// buffer will only be executable on that given queue family.
let command_buffer = AutoCommandBufferBuilder::primary_one_time_submit(device.clone(), 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
@@ -323,10 +313,7 @@ pub fn init(mut game: GameData) {
.begin_render_pass(framebuffers[image_num].clone(), false, clear_values).unwrap()
// We are now inside the first subpass of the render pass. We add a draw command.
//
// The last two parameters contain the list of resources to pass to the shaders.
// Since we used an `EmptyPipeline` object, the objects have to be `()`.
.draw(pipeline.clone(), &dynamic_state, mesh_vertex_buffer.clone(), (), game.push_constants.clone()).unwrap()
.draw(pipeline.clone(), &dynamic_state, mesh_vertex_buffer.clone(), (), data.push_constants.clone()).unwrap()
.draw(line_pipeline.clone(), &dynamic_state, line_vertex_buffer.clone(), (), ()).unwrap()
// We leave the render pass by calling `draw_end`. Note that if we had multiple
@@ -339,13 +326,6 @@ pub fn init(mut game: GameData) {
let future = previous_frame_end.join(acquire_future)
.then_execute(queue.clone(), command_buffer).unwrap()
// The color output is now expected to contain our triangle. But in order to show it on
// the screen, we have to *present* the image by calling `present`.
//
// This function does not actually present the image immediately. Instead it submits a
// present command at the end of the queue. This means that it will only be presented once
// the GPU has finished executing the command buffer that draws the triangle.
.then_swapchain_present(queue.clone(), swapchain.clone(), image_num)
.then_signal_fence_and_flush();
@@ -370,19 +350,16 @@ pub fn init(mut game: GameData) {
// 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.
// Handling the window events in order to close the program when the user wants to close
// it.
events_loop.poll_events(|ev| {
if !game.on_window_event(&ev) {
if !game.on_window_event(&mut data, &ev) {
match ev {
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => game.shutdown = true,
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => data.shutdown = true,
Event::WindowEvent { event: WindowEvent::Resized(_), .. } => recreate_swapchain = true,
_ => {}
}
}
});
if game.shutdown { return; }
if data.shutdown { return; }
}
}