stuff
This commit is contained in:
@@ -8,7 +8,9 @@ edition = "2018"
|
||||
vulkano-shaders = "0.13"
|
||||
vulkano = "0.13"
|
||||
vulkano-win = "0.13"
|
||||
shade_runner = "0.1.2"
|
||||
shaderc = "0.5.0"
|
||||
cgmath = "0.17"
|
||||
image = "0.21"
|
||||
winit = "0.19"
|
||||
time = "0.1.37"
|
||||
time = "0.1.37"
|
||||
|
||||
7
shaders/line.frag
Normal file
7
shaders/line.frag
Normal file
@@ -0,0 +1,7 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) out vec4 f_color;
|
||||
|
||||
void main() {
|
||||
f_color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
7
shaders/line.vert
Normal file
7
shaders/line.vert
Normal file
@@ -0,0 +1,7 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 position;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(position, 1.0);
|
||||
}
|
||||
@@ -3,5 +3,5 @@
|
||||
layout(location = 0) out vec4 f_color;
|
||||
|
||||
void main() {
|
||||
f_color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
f_color = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 position;
|
||||
layout(location = 0) in vec3 position;
|
||||
|
||||
layout(push_constant) uniform push_constants {
|
||||
float time;
|
||||
} push;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
gl_Position = vec4(position + vec3(sin(push.time / 10.), 0., 0.), 1.0);
|
||||
}
|
||||
44
src/main.rs
44
src/main.rs
@@ -1,5 +1,47 @@
|
||||
use crate::vulkan::{Vertex, GameData};
|
||||
use winit::{DeviceId, KeyboardInput, ElementState, VirtualKeyCode};
|
||||
use std::time::SystemTime;
|
||||
|
||||
mod vulkan;
|
||||
|
||||
impl GameData<'_> {
|
||||
fn on_init(self: &Self) {
|
||||
|
||||
}
|
||||
|
||||
fn on_keyboard_event(self: &Self, _: DeviceId, input: KeyboardInput) {
|
||||
if input.state == ElementState::Pressed && input.virtual_keycode == Some(VirtualKeyCode::F) {
|
||||
println!("doot");
|
||||
}
|
||||
}
|
||||
fn update_push_constants(self: &mut Self) {
|
||||
self.push_constants.time = self.start_time.elapsed().unwrap().as_millis() as f32 / 1000.0;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PushConstants {
|
||||
pub time: f32
|
||||
}
|
||||
|
||||
fn main() {
|
||||
vulkan::init();
|
||||
let mut pc = PushConstants {
|
||||
time: 0.0
|
||||
};
|
||||
|
||||
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: [-1., 0., 0.] },
|
||||
Vertex { position: [1., 0., 0.] },
|
||||
],
|
||||
push_constants: &mut pc,
|
||||
start_time: SystemTime::now()
|
||||
};
|
||||
|
||||
vulkan::init(data);
|
||||
}
|
||||
244
src/vulkan.rs
244
src/vulkan.rs
@@ -4,7 +4,8 @@ use vulkano::device::{Device, DeviceExtensions};
|
||||
use vulkano::framebuffer::{Framebuffer, FramebufferAbstract, Subpass, RenderPassAbstract};
|
||||
use vulkano::image::SwapchainImage;
|
||||
use vulkano::instance::{Instance, PhysicalDevice};
|
||||
use vulkano::pipeline::GraphicsPipeline;
|
||||
use vulkano::pipeline::{GraphicsPipeline};
|
||||
use vulkano::pipeline::shader::{GraphicsShaderType, ShaderModule};
|
||||
use vulkano::pipeline::viewport::Viewport;
|
||||
use vulkano::swapchain::{AcquireError, PresentMode, SurfaceTransform, Swapchain, SwapchainCreationError};
|
||||
use vulkano::swapchain;
|
||||
@@ -16,39 +17,49 @@ use vulkano_win::VkSurfaceBuild;
|
||||
use winit::{EventsLoop, Window, WindowBuilder, Event, WindowEvent};
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::SystemTime;
|
||||
use std::path::PathBuf;
|
||||
use std::ffi::CStr;
|
||||
|
||||
mod vs {
|
||||
vulkano_shaders::shader!{
|
||||
ty: "vertex",
|
||||
path: "shaders/triangle.vert",
|
||||
}
|
||||
use shade_runner;
|
||||
use shade_runner::{CompiledShaders, Entry};
|
||||
|
||||
use shaderc;
|
||||
use vulkano::pipeline::vertex::SingleBufferDefinition;
|
||||
use vulkano::descriptor::PipelineLayoutAbstract;
|
||||
use crate::PushConstants;
|
||||
|
||||
const VALIDATION_LAYERS: &[&str] = &[
|
||||
"VK_LAYER_LUNARG_standard_validation"
|
||||
];
|
||||
|
||||
#[cfg(all(debug_assertions))]
|
||||
const ENABLE_VALIDATION_LAYERS: bool = true;
|
||||
#[cfg(not(debug_assertions))]
|
||||
const ENABLE_VALIDATION_LAYERS: bool = false;
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct Vertex {
|
||||
pub position: [f32; 3],
|
||||
}
|
||||
vulkano::impl_vertex!(Vertex, position);
|
||||
|
||||
pub struct GameData<'a> {
|
||||
pub start_time: SystemTime,
|
||||
pub mesh_vertices: Vec<Vertex>,
|
||||
pub line_vertices: Vec<Vertex>,
|
||||
pub push_constants: &'a mut PushConstants,
|
||||
}
|
||||
|
||||
mod fs {
|
||||
vulkano_shaders::shader!{
|
||||
ty: "fragment",
|
||||
path: "shaders/triangle.frag",
|
||||
}
|
||||
}
|
||||
|
||||
mod line_vs {
|
||||
vulkano_shaders::shader!{
|
||||
ty: "vertex",
|
||||
path: "shaders/triangle.vert",
|
||||
}
|
||||
}
|
||||
|
||||
mod line_fs {
|
||||
vulkano_shaders::shader!{
|
||||
ty: "fragment",
|
||||
path: "shaders/triangle.frag",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
pub fn init(mut game: GameData) {
|
||||
let instance = {
|
||||
let extensions = vulkano_win::required_extensions();
|
||||
Instance::new(None, &extensions, None).unwrap()
|
||||
|
||||
if ENABLE_VALIDATION_LAYERS {
|
||||
Instance::new(None, &extensions, VALIDATION_LAYERS.iter().cloned()).expect("failed to create Vulkan instance")
|
||||
} else {
|
||||
Instance::new(None, &extensions, None).expect("failed to create Vulkan instance")
|
||||
}
|
||||
};
|
||||
let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
|
||||
println!("Using device: {} (type: {:?})", physical.name(), physical.ty());
|
||||
@@ -133,7 +144,7 @@ pub fn init() {
|
||||
[dimensions.0, dimensions.1]
|
||||
} else {
|
||||
// The window no longer exists so exit the application.
|
||||
return;
|
||||
panic!("idk");
|
||||
};
|
||||
|
||||
// Please take a look at the docs for the meaning of the parameters we didn't mention.
|
||||
@@ -143,24 +154,8 @@ pub fn init() {
|
||||
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
struct Vertex { position: [f32; 2] }
|
||||
vulkano::impl_vertex!(Vertex, position);
|
||||
// We now create a buffer that will store the shape of our triangle.
|
||||
let vertex_buffer = {
|
||||
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), [
|
||||
Vertex { position: [-0.5, -0.25] },
|
||||
Vertex { position: [0.0, 0.5] },
|
||||
Vertex { position: [0.25, -0.1] }
|
||||
].iter().cloned()).unwrap()
|
||||
};
|
||||
|
||||
let line_vertex_buffer = {
|
||||
CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), [
|
||||
Vertex { position: [-0.4, -0.3] },
|
||||
Vertex { position: [0.01, 0.55] },
|
||||
].iter().cloned()).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.mesh_vertices.iter().cloned()).unwrap();
|
||||
|
||||
// The next step is to create the shaders.
|
||||
//
|
||||
@@ -206,44 +201,38 @@ pub fn init() {
|
||||
}
|
||||
).unwrap());
|
||||
|
||||
let vs = vs::Shader::load(device.clone()).unwrap();
|
||||
let fs = fs::Shader::load(device.clone()).unwrap();
|
||||
|
||||
let line_vs = line_vs::Shader::load(device.clone()).unwrap();
|
||||
let line_fs = line_fs::Shader::load(device.clone()).unwrap();
|
||||
|
||||
let sub_pass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||
|
||||
// Before we draw we have to create what is called a pipeline. This is similar to an OpenGL
|
||||
// program, but much more specific.
|
||||
let pipeline = Arc::new(GraphicsPipeline::start()
|
||||
// We need to indicate the layout of the vertices.
|
||||
// The type `SingleBufferDefinition` actually contains a template parameter corresponding
|
||||
// to the type of each vertex. But in this code it is automatically inferred.
|
||||
.vertex_input_single_buffer()
|
||||
// A Vulkan shader can in theory contain multiple entry points, so we have to specify
|
||||
// which one. The `main` word of `main_entry_point` actually corresponds to the name of
|
||||
// the entry point.
|
||||
.vertex_shader(vs.main_entry_point(), ())
|
||||
// The content of the vertex buffer describes a list of triangles.
|
||||
.triangle_list()
|
||||
// Use a resizable viewport set to draw over the entire window
|
||||
.viewports_dynamic_scissors_irrelevant(1)
|
||||
// See `vertex_shader`.
|
||||
.fragment_shader(fs.main_entry_point(), ())
|
||||
// We have to indicate which subpass of which render pass this pipeline is going to be used
|
||||
// in. The pipeline will only be usable from this particular subpass.
|
||||
.render_pass(sub_pass.clone())
|
||||
// Now that our builder is filled, we call `build()` to obtain an actual pipeline.
|
||||
.build(device.clone())
|
||||
.unwrap());
|
||||
let pipeline = create_pipeline(device.clone(), sub_pass.clone());
|
||||
|
||||
let line_shader_vertex_entry;
|
||||
let line_shader_fragment_entry;
|
||||
let line_shader_module_vertex;
|
||||
let line_shader_module_fragment;
|
||||
let (line_shader, line_shader_data) = read_shader("shaders/line.vert", "shaders/line.frag");
|
||||
unsafe {
|
||||
line_shader_module_vertex = ShaderModule::from_words(device.clone(), &line_shader.vertex).expect("Failed to load");
|
||||
line_shader_vertex_entry = line_shader_module_vertex.graphics_entry_point(
|
||||
CStr::from_bytes_with_nul_unchecked(b"main\0"),
|
||||
line_shader_data.vert_input,
|
||||
line_shader_data.vert_output,
|
||||
line_shader_data.vert_layout,
|
||||
GraphicsShaderType::Vertex);
|
||||
line_shader_module_fragment = ShaderModule::from_words(device.clone(), &line_shader.fragment).expect("Failed to load");
|
||||
line_shader_fragment_entry = line_shader_module_fragment.graphics_entry_point(
|
||||
CStr::from_bytes_with_nul_unchecked(b"main\0"),
|
||||
line_shader_data.frag_input,
|
||||
line_shader_data.frag_output,
|
||||
line_shader_data.frag_layout,
|
||||
GraphicsShaderType::Fragment);
|
||||
};
|
||||
|
||||
let line_pipeline = Arc::new(GraphicsPipeline::start()
|
||||
.vertex_input_single_buffer()
|
||||
.vertex_shader(line_vs.main_entry_point(), ())
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
.vertex_shader(line_shader_vertex_entry.clone(), ())
|
||||
.line_list()
|
||||
.viewports_dynamic_scissors_irrelevant(1)
|
||||
.fragment_shader(line_fs.main_entry_point(), ())
|
||||
.fragment_shader(line_shader_fragment_entry.clone(), ())
|
||||
.render_pass(sub_pass.clone())
|
||||
.build(device.clone())
|
||||
.unwrap());
|
||||
@@ -258,10 +247,8 @@ pub fn init() {
|
||||
// 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(&images, render_pass.clone(), &mut dynamic_state);
|
||||
|
||||
// Initialization is finally finished!
|
||||
|
||||
// In some situations, the swapchain will become invalid by itself. This includes for example
|
||||
|
||||
// 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.
|
||||
@@ -333,6 +320,8 @@ pub fn init() {
|
||||
// Specify the color to clear the framebuffer with i.e. blue
|
||||
let clear_values = vec!([0.0, 0.0, 1.0, 1.0].into());
|
||||
|
||||
game.update_push_constants();
|
||||
|
||||
// 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.
|
||||
//
|
||||
@@ -350,23 +339,19 @@ pub fn init() {
|
||||
// The third parameter builds the list of values to clear the attachments with. The API
|
||||
// is similar to the list of attachments when building the framebuffers, except that
|
||||
// only the attachments that use `load: Clear` appear in the list.
|
||||
.begin_render_pass(framebuffers[image_num].clone(), false, clear_values)
|
||||
.unwrap()
|
||||
.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, vertex_buffer.clone(), (), ())
|
||||
.unwrap()
|
||||
.draw(line_pipeline.clone(), &dynamic_state, line_vertex_buffer.clone(), (), ())
|
||||
.unwrap()
|
||||
.draw(pipeline.clone(), &dynamic_state, mesh_vertex_buffer.clone(), (), game.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
|
||||
// subpasses we could have called `next_inline` (or `next_secondary`) to jump to the
|
||||
// next subpass.
|
||||
.end_render_pass()
|
||||
.unwrap()
|
||||
.end_render_pass().unwrap()
|
||||
|
||||
// Finish building the command buffer by calling `build`.
|
||||
.build().unwrap();
|
||||
@@ -412,7 +397,10 @@ pub fn init() {
|
||||
match ev {
|
||||
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => done = true,
|
||||
Event::WindowEvent { event: WindowEvent::Resized(_), .. } => recreate_swapchain = true,
|
||||
_ => ()
|
||||
Event::WindowEvent { event: WindowEvent::KeyboardInput { device_id, input }, .. } => {
|
||||
game.on_keyboard_event(device_id, input);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
if done { return; }
|
||||
@@ -442,3 +430,73 @@ fn window_size_dependent_setup(
|
||||
) as Arc<dyn FramebufferAbstract + Send + Sync>
|
||||
}).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn create_pipeline<T: RenderPassAbstract>(device: Arc<Device>, sub_pass: Subpass<Arc<T>>) -> Arc<GraphicsPipeline<SingleBufferDefinition<Vertex>, Box<PipelineLayoutAbstract + Send + Sync>, Arc<T>>> {
|
||||
let mesh_shader_vertex_entry;
|
||||
let mesh_shader_fragment_entry;
|
||||
let mesh_shader_module_vertex;
|
||||
let mesh_shader_module_fragment;
|
||||
let (mesh_shader, mesh_shader_data) = read_shader("shaders/triangle.vert", "shaders/triangle.frag");
|
||||
unsafe {
|
||||
mesh_shader_module_vertex = ShaderModule::from_words(device.clone(), &mesh_shader.vertex).expect("Failed to load");
|
||||
mesh_shader_vertex_entry = mesh_shader_module_vertex.graphics_entry_point(
|
||||
CStr::from_bytes_with_nul_unchecked(b"main\0"),
|
||||
mesh_shader_data.vert_input,
|
||||
mesh_shader_data.vert_output,
|
||||
mesh_shader_data.vert_layout,
|
||||
GraphicsShaderType::Vertex);
|
||||
mesh_shader_module_fragment = ShaderModule::from_words(device.clone(), &mesh_shader.fragment).expect("Failed to load");
|
||||
mesh_shader_fragment_entry = mesh_shader_module_fragment.graphics_entry_point(
|
||||
CStr::from_bytes_with_nul_unchecked(b"main\0"),
|
||||
mesh_shader_data.frag_input,
|
||||
mesh_shader_data.frag_output,
|
||||
mesh_shader_data.frag_layout,
|
||||
GraphicsShaderType::Fragment);
|
||||
};
|
||||
|
||||
// Before we draw we have to create what is called a pipeline. This is similar to an OpenGL
|
||||
// program, but much more specific.
|
||||
let pipeline = Arc::new(GraphicsPipeline::start()
|
||||
// We need to indicate the layout of the vertices.
|
||||
.vertex_input_single_buffer::<Vertex>()
|
||||
// A Vulkan shader can in theory contain multiple entry points, so we have to specify
|
||||
// which one. The `main` word of `main_entry_point` actually corresponds to the name of
|
||||
// the entry point.
|
||||
.vertex_shader(mesh_shader_vertex_entry.clone(), ())
|
||||
// The content of the vertex buffer describes a list of triangles.
|
||||
.triangle_list()
|
||||
// Use a resizable viewport set to draw over the entire window
|
||||
.viewports_dynamic_scissors_irrelevant(1)
|
||||
// See `vertex_shader`.
|
||||
.fragment_shader(mesh_shader_fragment_entry.clone(), ())
|
||||
// We have to indicate which subpass of which render pass this pipeline is going to be used
|
||||
// in. The pipeline will only be usable from this particular subpass.
|
||||
.render_pass(sub_pass.clone())
|
||||
// Now that our builder is filled, we call `build()` to obtain an actual pipeline.
|
||||
.build(device.clone())
|
||||
.unwrap());
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
fn read_shader(vert_path_relative: &str, frag_path_relative: &str) -> (CompiledShaders, Entry) {
|
||||
let project_root = std::env::current_dir().expect("failed to get root directory");
|
||||
|
||||
let mut vert_path = project_root.clone();
|
||||
vert_path.push(PathBuf::from(vert_path_relative));
|
||||
|
||||
let mut frag_path = project_root.clone();
|
||||
frag_path.push(PathBuf::from(frag_path_relative));
|
||||
|
||||
let shader_result = shade_runner::load(vert_path, frag_path);
|
||||
match shader_result {
|
||||
Ok(shader) => {
|
||||
let shader_data = shade_runner::parse(&shader).expect("Failed to parse");
|
||||
return (shader, shader_data);
|
||||
}
|
||||
Err(shade_runner::error::Error::Compile(shade_runner::error::CompileError::Compile(shaderc::Error::CompilationError(line, error)))) => {
|
||||
panic!("Shader line {}: {:?}", line, error);
|
||||
}
|
||||
Err(error) => panic!("Shader compilation error: {:?}", error)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user