commit
1d23169dcc
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
Cargo.lock
|
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "tri"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
winit = "0.20.0"
|
||||||
|
ash = "0.29.0"
|
||||||
|
num = "0.2"
|
||||||
|
cgmath = "0.17.0"
|
||||||
|
image = "0.22"
|
||||||
|
memoffset = "0.5.1"
|
||||||
|
tobj = "0.1.10"
|
@ -0,0 +1,49 @@
|
|||||||
|
use std::process::Command;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
fn main() -> std::io::Result<()> {
|
||||||
|
println!("building shaders...");
|
||||||
|
//shaders path
|
||||||
|
let shaders = Path::new("./src/shaders");
|
||||||
|
//shader target path
|
||||||
|
let out = Command::new("mkdir")
|
||||||
|
.arg("target/shaders/")
|
||||||
|
.output();
|
||||||
|
|
||||||
|
let shader_target = Path::new("./target/shaders/");
|
||||||
|
//compile all glsl shaders
|
||||||
|
for entry in shaders.read_dir().expect("reading shader directory failed") {
|
||||||
|
if let Ok(entry) = entry {
|
||||||
|
let shader_path = entry.path();
|
||||||
|
println!("compiling shader: {:?}", shader_path);
|
||||||
|
let shader_path_string: String = "./target/shaders/".to_string() + shader_path.file_name().unwrap().to_str().unwrap() + ".spv";
|
||||||
|
let shader_file: &OsStr = OsStr::new::<str>(shader_path_string.as_str());
|
||||||
|
let out = Command::new("glslc")
|
||||||
|
.arg("-c")
|
||||||
|
.arg(shader_path)
|
||||||
|
.arg("-o")
|
||||||
|
.arg(shader_file)
|
||||||
|
.output();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//include all compiled shaders in shaders.rs file in src dir
|
||||||
|
let mut txt: String = String::new();
|
||||||
|
for entry in shader_target.read_dir().expect("reading compiled shader target directory failed") {
|
||||||
|
if let Ok(entry) = entry {
|
||||||
|
let bin_path = entry.path();
|
||||||
|
let bin_path_string = bin_path.file_name().unwrap().to_str().unwrap().to_string();
|
||||||
|
txt += &("const ".to_owned() +
|
||||||
|
&bin_path_string.replace(".spv", "").replace(".", "_").to_uppercase() +
|
||||||
|
" = include_bytes!(\"../target/shaders/" +
|
||||||
|
&bin_path_string +
|
||||||
|
"\");\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut file = File::create("./src/shaders.rs")?;
|
||||||
|
file.write_all(txt.as_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -0,0 +1,887 @@
|
|||||||
|
pub mod utility;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
utility::constants::*,
|
||||||
|
utility::debug::*,
|
||||||
|
utility::share,
|
||||||
|
utility::structures::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
use ash::version::DeviceV1_0;
|
||||||
|
use ash::version::InstanceV1_0;
|
||||||
|
use ash::vk;
|
||||||
|
use memoffset::offset_of;
|
||||||
|
use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent};
|
||||||
|
use winit::event_loop::{EventLoop, ControlFlow};
|
||||||
|
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
const WINDOW_TITLE: &'static str = "18.Vertex Buffer";
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Debug, Copy)]
|
||||||
|
struct Vertex {
|
||||||
|
pos: [f32; 2],
|
||||||
|
color: [f32; 3],
|
||||||
|
}
|
||||||
|
impl Vertex {
|
||||||
|
fn get_binding_description() -> [vk::VertexInputBindingDescription; 1] {
|
||||||
|
[vk::VertexInputBindingDescription {
|
||||||
|
binding: 0,
|
||||||
|
stride: std::mem::size_of::<Vertex>() as u32,
|
||||||
|
input_rate: vk::VertexInputRate::VERTEX,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_attribute_descriptions() -> [vk::VertexInputAttributeDescription; 2] {
|
||||||
|
[
|
||||||
|
vk::VertexInputAttributeDescription {
|
||||||
|
binding: 0,
|
||||||
|
location: 0,
|
||||||
|
format: vk::Format::R32G32_SFLOAT,
|
||||||
|
offset: offset_of!(Vertex, pos) as u32,
|
||||||
|
},
|
||||||
|
vk::VertexInputAttributeDescription {
|
||||||
|
binding: 0,
|
||||||
|
location: 1,
|
||||||
|
format: vk::Format::R32G32B32_SFLOAT,
|
||||||
|
offset: offset_of!(Vertex, color) as u32,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VERTICES_DATA: [Vertex; 3] = [
|
||||||
|
Vertex {
|
||||||
|
pos: [0.0, -0.5],
|
||||||
|
color: [1.0, 1.0, 1.0],
|
||||||
|
},
|
||||||
|
Vertex {
|
||||||
|
pos: [0.5, 0.5],
|
||||||
|
color: [0.0, 1.0, 0.0],
|
||||||
|
},
|
||||||
|
Vertex {
|
||||||
|
pos: [-0.5, 0.5],
|
||||||
|
color: [0.0, 0.0, 1.0],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
struct VulkanApp {
|
||||||
|
window: winit::window::Window,
|
||||||
|
|
||||||
|
// vulkan stuff
|
||||||
|
_entry: ash::Entry,
|
||||||
|
instance: ash::Instance,
|
||||||
|
surface_loader: ash::extensions::khr::Surface,
|
||||||
|
surface: vk::SurfaceKHR,
|
||||||
|
debug_utils_loader: ash::extensions::ext::DebugUtils,
|
||||||
|
debug_merssager: vk::DebugUtilsMessengerEXT,
|
||||||
|
|
||||||
|
physical_device: vk::PhysicalDevice,
|
||||||
|
device: ash::Device,
|
||||||
|
|
||||||
|
queue_family: QueueFamilyIndices,
|
||||||
|
graphics_queue: vk::Queue,
|
||||||
|
present_queue: vk::Queue,
|
||||||
|
|
||||||
|
swapchain_loader: ash::extensions::khr::Swapchain,
|
||||||
|
swapchain: vk::SwapchainKHR,
|
||||||
|
swapchain_images: Vec<vk::Image>,
|
||||||
|
swapchain_format: vk::Format,
|
||||||
|
swapchain_extent: vk::Extent2D,
|
||||||
|
swapchain_imageviews: Vec<vk::ImageView>,
|
||||||
|
swapchain_framebuffers: Vec<vk::Framebuffer>,
|
||||||
|
|
||||||
|
render_pass: vk::RenderPass,
|
||||||
|
pipeline_layout: vk::PipelineLayout,
|
||||||
|
graphics_pipeline: vk::Pipeline,
|
||||||
|
|
||||||
|
vertex_buffer: vk::Buffer,
|
||||||
|
vertex_buffer_memory: vk::DeviceMemory,
|
||||||
|
|
||||||
|
command_pool: vk::CommandPool,
|
||||||
|
command_buffers: Vec<vk::CommandBuffer>,
|
||||||
|
|
||||||
|
image_available_semaphores: Vec<vk::Semaphore>,
|
||||||
|
render_finished_semaphores: Vec<vk::Semaphore>,
|
||||||
|
in_flight_fences: Vec<vk::Fence>,
|
||||||
|
current_frame: usize,
|
||||||
|
|
||||||
|
is_framebuffer_resized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VulkanApp {
|
||||||
|
pub fn new(event_loop: &winit::event_loop::EventLoop<()>) -> VulkanApp {
|
||||||
|
|
||||||
|
let window = utility::window::init_window(event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||||
|
|
||||||
|
// init vulkan stuff
|
||||||
|
let entry = ash::Entry::new().unwrap();
|
||||||
|
let instance = share::create_instance(
|
||||||
|
&entry,
|
||||||
|
WINDOW_TITLE,
|
||||||
|
VALIDATION.is_enable,
|
||||||
|
&VALIDATION.required_validation_layers.to_vec(),
|
||||||
|
);
|
||||||
|
let surface_stuff =
|
||||||
|
share::create_surface(&entry, &instance, &window, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||||
|
let (debug_utils_loader, debug_merssager) =
|
||||||
|
setup_debug_utils(VALIDATION.is_enable, &entry, &instance);
|
||||||
|
let physical_device =
|
||||||
|
share::pick_physical_device(&instance, &surface_stuff, &DEVICE_EXTENSIONS);
|
||||||
|
let (device, queue_family) = share::create_logical_device(
|
||||||
|
&instance,
|
||||||
|
physical_device,
|
||||||
|
&VALIDATION,
|
||||||
|
&DEVICE_EXTENSIONS,
|
||||||
|
&surface_stuff,
|
||||||
|
);
|
||||||
|
let graphics_queue =
|
||||||
|
unsafe { device.get_device_queue(queue_family.graphics_family.unwrap(), 0) };
|
||||||
|
let present_queue =
|
||||||
|
unsafe { device.get_device_queue(queue_family.present_family.unwrap(), 0) };
|
||||||
|
let swapchain_stuff = share::create_swapchain(
|
||||||
|
&instance,
|
||||||
|
&device,
|
||||||
|
physical_device,
|
||||||
|
&window,
|
||||||
|
&surface_stuff,
|
||||||
|
&queue_family,
|
||||||
|
);
|
||||||
|
let swapchain_imageviews = share::v1::create_image_views(
|
||||||
|
&device,
|
||||||
|
swapchain_stuff.swapchain_format,
|
||||||
|
&swapchain_stuff.swapchain_images,
|
||||||
|
);
|
||||||
|
let render_pass = share::v1::create_render_pass(&device, swapchain_stuff.swapchain_format);
|
||||||
|
let (graphics_pipeline, pipeline_layout) = VulkanApp::create_graphics_pipeline(
|
||||||
|
&device,
|
||||||
|
render_pass,
|
||||||
|
swapchain_stuff.swapchain_extent,
|
||||||
|
);
|
||||||
|
let swapchain_framebuffers = share::v1::create_framebuffers(
|
||||||
|
&device,
|
||||||
|
render_pass,
|
||||||
|
&swapchain_imageviews,
|
||||||
|
swapchain_stuff.swapchain_extent,
|
||||||
|
);
|
||||||
|
let command_pool = share::v1::create_command_pool(&device, &queue_family);
|
||||||
|
let (vertex_buffer, vertex_buffer_memory) =
|
||||||
|
VulkanApp::create_vertex_buffer(&instance, &device, physical_device);
|
||||||
|
let command_buffers = VulkanApp::create_command_buffers(
|
||||||
|
&device,
|
||||||
|
command_pool,
|
||||||
|
graphics_pipeline,
|
||||||
|
&swapchain_framebuffers,
|
||||||
|
render_pass,
|
||||||
|
swapchain_stuff.swapchain_extent,
|
||||||
|
vertex_buffer,
|
||||||
|
);
|
||||||
|
let sync_ojbects = share::v1::create_sync_objects(&device, MAX_FRAMES_IN_FLIGHT);
|
||||||
|
|
||||||
|
// cleanup(); the 'drop' function will take care of it.
|
||||||
|
VulkanApp {
|
||||||
|
// winit stuff
|
||||||
|
window,
|
||||||
|
|
||||||
|
// vulkan stuff
|
||||||
|
_entry: entry,
|
||||||
|
instance,
|
||||||
|
surface: surface_stuff.surface,
|
||||||
|
surface_loader: surface_stuff.surface_loader,
|
||||||
|
debug_utils_loader,
|
||||||
|
debug_merssager,
|
||||||
|
|
||||||
|
physical_device,
|
||||||
|
device,
|
||||||
|
|
||||||
|
queue_family,
|
||||||
|
graphics_queue,
|
||||||
|
present_queue,
|
||||||
|
|
||||||
|
swapchain_loader: swapchain_stuff.swapchain_loader,
|
||||||
|
swapchain: swapchain_stuff.swapchain,
|
||||||
|
swapchain_format: swapchain_stuff.swapchain_format,
|
||||||
|
swapchain_images: swapchain_stuff.swapchain_images,
|
||||||
|
swapchain_extent: swapchain_stuff.swapchain_extent,
|
||||||
|
swapchain_imageviews,
|
||||||
|
swapchain_framebuffers,
|
||||||
|
|
||||||
|
pipeline_layout,
|
||||||
|
render_pass,
|
||||||
|
graphics_pipeline,
|
||||||
|
|
||||||
|
vertex_buffer,
|
||||||
|
vertex_buffer_memory,
|
||||||
|
|
||||||
|
command_pool,
|
||||||
|
command_buffers,
|
||||||
|
|
||||||
|
image_available_semaphores: sync_ojbects.image_available_semaphores,
|
||||||
|
render_finished_semaphores: sync_ojbects.render_finished_semaphores,
|
||||||
|
in_flight_fences: sync_ojbects.inflight_fences,
|
||||||
|
current_frame: 0,
|
||||||
|
|
||||||
|
is_framebuffer_resized: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_vertex_buffer(
|
||||||
|
instance: &ash::Instance,
|
||||||
|
device: &ash::Device,
|
||||||
|
physical_device: vk::PhysicalDevice,
|
||||||
|
) -> (vk::Buffer, vk::DeviceMemory) {
|
||||||
|
let vertex_buffer_create_info = vk::BufferCreateInfo {
|
||||||
|
s_type: vk::StructureType::BUFFER_CREATE_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: vk::BufferCreateFlags::empty(),
|
||||||
|
size: std::mem::size_of_val(&VERTICES_DATA) as u64,
|
||||||
|
usage: vk::BufferUsageFlags::VERTEX_BUFFER,
|
||||||
|
sharing_mode: vk::SharingMode::EXCLUSIVE,
|
||||||
|
queue_family_index_count: 0,
|
||||||
|
p_queue_family_indices: ptr::null(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let vertex_buffer = unsafe {
|
||||||
|
device
|
||||||
|
.create_buffer(&vertex_buffer_create_info, None)
|
||||||
|
.expect("Failed to create Vertex Buffer")
|
||||||
|
};
|
||||||
|
|
||||||
|
let mem_requirements = unsafe { device.get_buffer_memory_requirements(vertex_buffer) };
|
||||||
|
let mem_properties =
|
||||||
|
unsafe { instance.get_physical_device_memory_properties(physical_device) };
|
||||||
|
let required_memory_flags: vk::MemoryPropertyFlags =
|
||||||
|
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT;
|
||||||
|
let memory_type = VulkanApp::find_memory_type(
|
||||||
|
mem_requirements.memory_type_bits,
|
||||||
|
required_memory_flags,
|
||||||
|
mem_properties,
|
||||||
|
);
|
||||||
|
|
||||||
|
let allocate_info = vk::MemoryAllocateInfo {
|
||||||
|
s_type: vk::StructureType::MEMORY_ALLOCATE_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
allocation_size: mem_requirements.size,
|
||||||
|
memory_type_index: memory_type,
|
||||||
|
};
|
||||||
|
|
||||||
|
let vertex_buffer_memory = unsafe {
|
||||||
|
device
|
||||||
|
.allocate_memory(&allocate_info, None)
|
||||||
|
.expect("Failed to allocate vertex buffer memory!")
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
device
|
||||||
|
.bind_buffer_memory(vertex_buffer, vertex_buffer_memory, 0)
|
||||||
|
.expect("Failed to bind Buffer");
|
||||||
|
|
||||||
|
let data_ptr = device
|
||||||
|
.map_memory(
|
||||||
|
vertex_buffer_memory,
|
||||||
|
0,
|
||||||
|
vertex_buffer_create_info.size,
|
||||||
|
vk::MemoryMapFlags::empty(),
|
||||||
|
)
|
||||||
|
.expect("Failed to Map Memory") as *mut Vertex;
|
||||||
|
|
||||||
|
data_ptr.copy_from_nonoverlapping(VERTICES_DATA.as_ptr(), VERTICES_DATA.len());
|
||||||
|
|
||||||
|
device.unmap_memory(vertex_buffer_memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
(vertex_buffer, vertex_buffer_memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_memory_type(
|
||||||
|
type_filter: u32,
|
||||||
|
required_properties: vk::MemoryPropertyFlags,
|
||||||
|
mem_properties: vk::PhysicalDeviceMemoryProperties,
|
||||||
|
) -> u32 {
|
||||||
|
for (i, memory_type) in mem_properties.memory_types.iter().enumerate() {
|
||||||
|
//if (type_filter & (1 << i)) > 0 && (memory_type.property_flags & required_properties) == required_properties {
|
||||||
|
// return i as u32
|
||||||
|
// }
|
||||||
|
|
||||||
|
// same implementation
|
||||||
|
if (type_filter & (1 << i)) > 0
|
||||||
|
&& memory_type.property_flags.contains(required_properties)
|
||||||
|
{
|
||||||
|
return i as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("Failed to find suitable memory type!")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_command_buffers(
|
||||||
|
device: &ash::Device,
|
||||||
|
command_pool: vk::CommandPool,
|
||||||
|
graphics_pipeline: vk::Pipeline,
|
||||||
|
framebuffers: &Vec<vk::Framebuffer>,
|
||||||
|
render_pass: vk::RenderPass,
|
||||||
|
surface_extent: vk::Extent2D,
|
||||||
|
vertex_buffer: vk::Buffer,
|
||||||
|
) -> Vec<vk::CommandBuffer> {
|
||||||
|
let command_buffer_allocate_info = vk::CommandBufferAllocateInfo {
|
||||||
|
s_type: vk::StructureType::COMMAND_BUFFER_ALLOCATE_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
command_buffer_count: framebuffers.len() as u32,
|
||||||
|
command_pool,
|
||||||
|
level: vk::CommandBufferLevel::PRIMARY,
|
||||||
|
};
|
||||||
|
|
||||||
|
let command_buffers = unsafe {
|
||||||
|
device
|
||||||
|
.allocate_command_buffers(&command_buffer_allocate_info)
|
||||||
|
.expect("Failed to allocate Command Buffers!")
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i, &command_buffer) in command_buffers.iter().enumerate() {
|
||||||
|
let command_buffer_begin_info = vk::CommandBufferBeginInfo {
|
||||||
|
s_type: vk::StructureType::COMMAND_BUFFER_BEGIN_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: vk::CommandBufferUsageFlags::SIMULTANEOUS_USE,
|
||||||
|
p_inheritance_info: ptr::null(),
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
device
|
||||||
|
.begin_command_buffer(command_buffer, &command_buffer_begin_info)
|
||||||
|
.expect("Failed to begin recording Command Buffer at beginning!");
|
||||||
|
}
|
||||||
|
|
||||||
|
let clear_values = [vk::ClearValue {
|
||||||
|
color: vk::ClearColorValue {
|
||||||
|
float32: [0.0, 0.0, 0.0, 1.0],
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
|
||||||
|
let render_pass_begin_info = vk::RenderPassBeginInfo {
|
||||||
|
s_type: vk::StructureType::RENDER_PASS_BEGIN_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
framebuffer: framebuffers[i],
|
||||||
|
render_pass,
|
||||||
|
clear_value_count: clear_values.len() as u32,
|
||||||
|
p_clear_values: clear_values.as_ptr(),
|
||||||
|
render_area: vk::Rect2D {
|
||||||
|
offset: vk::Offset2D { x: 0, y: 0 },
|
||||||
|
extent: surface_extent,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
device.cmd_begin_render_pass(
|
||||||
|
command_buffer,
|
||||||
|
&render_pass_begin_info,
|
||||||
|
vk::SubpassContents::INLINE,
|
||||||
|
);
|
||||||
|
device.cmd_bind_pipeline(
|
||||||
|
command_buffer,
|
||||||
|
vk::PipelineBindPoint::GRAPHICS,
|
||||||
|
graphics_pipeline,
|
||||||
|
);
|
||||||
|
|
||||||
|
let vertex_buffers = [vertex_buffer];
|
||||||
|
let offsets = [0_u64];
|
||||||
|
|
||||||
|
device.cmd_bind_vertex_buffers(command_buffer, 0, &vertex_buffers, &offsets);
|
||||||
|
|
||||||
|
device.cmd_draw(command_buffer, VERTICES_DATA.len() as u32, 1, 0, 0);
|
||||||
|
|
||||||
|
device.cmd_end_render_pass(command_buffer);
|
||||||
|
|
||||||
|
device
|
||||||
|
.end_command_buffer(command_buffer)
|
||||||
|
.expect("Failed to record Command Buffer at Ending!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
command_buffers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix content -------------------------------------------------------------------------------
|
||||||
|
impl VulkanApp {
|
||||||
|
fn create_graphics_pipeline(
|
||||||
|
device: &ash::Device,
|
||||||
|
render_pass: vk::RenderPass,
|
||||||
|
swapchain_extent: vk::Extent2D,
|
||||||
|
) -> (vk::Pipeline, vk::PipelineLayout) {
|
||||||
|
let vert_shader_module = share::create_shader_module(
|
||||||
|
device,
|
||||||
|
include_bytes!("../target/shaders/tri.vert.spv").to_vec(),
|
||||||
|
);
|
||||||
|
let frag_shader_module = share::create_shader_module(
|
||||||
|
device,
|
||||||
|
include_bytes!("../target/shaders/tri.frag.spv").to_vec(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let main_function_name = CString::new("main").unwrap(); // the beginning function name in shader code.
|
||||||
|
|
||||||
|
let shader_stages = [
|
||||||
|
vk::PipelineShaderStageCreateInfo {
|
||||||
|
// Vertex Shader
|
||||||
|
s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: vk::PipelineShaderStageCreateFlags::empty(),
|
||||||
|
module: vert_shader_module,
|
||||||
|
p_name: main_function_name.as_ptr(),
|
||||||
|
stage: vk::ShaderStageFlags::VERTEX,
|
||||||
|
p_specialization_info: ptr::null(),
|
||||||
|
},
|
||||||
|
vk::PipelineShaderStageCreateInfo {
|
||||||
|
// Fragment Shader
|
||||||
|
s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: vk::PipelineShaderStageCreateFlags::empty(),
|
||||||
|
module: frag_shader_module,
|
||||||
|
p_name: main_function_name.as_ptr(),
|
||||||
|
stage: vk::ShaderStageFlags::FRAGMENT,
|
||||||
|
p_specialization_info: ptr::null(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let binding_description = Vertex::get_binding_description();
|
||||||
|
let attribute_description = Vertex::get_attribute_descriptions();
|
||||||
|
|
||||||
|
let vertex_input_state_create_info = vk::PipelineVertexInputStateCreateInfo {
|
||||||
|
s_type: vk::StructureType::PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: vk::PipelineVertexInputStateCreateFlags::empty(),
|
||||||
|
vertex_attribute_description_count: attribute_description.len() as u32,
|
||||||
|
p_vertex_attribute_descriptions: attribute_description.as_ptr(),
|
||||||
|
vertex_binding_description_count: binding_description.len() as u32,
|
||||||
|
p_vertex_binding_descriptions: binding_description.as_ptr(),
|
||||||
|
};
|
||||||
|
let vertex_input_assembly_state_info = vk::PipelineInputAssemblyStateCreateInfo {
|
||||||
|
s_type: vk::StructureType::PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||||
|
flags: vk::PipelineInputAssemblyStateCreateFlags::empty(),
|
||||||
|
p_next: ptr::null(),
|
||||||
|
topology: vk::PrimitiveTopology::TRIANGLE_LIST,
|
||||||
|
primitive_restart_enable: vk::FALSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewports = [vk::Viewport {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
width: swapchain_extent.width as f32,
|
||||||
|
height: swapchain_extent.height as f32,
|
||||||
|
min_depth: 0.0,
|
||||||
|
max_depth: 1.0,
|
||||||
|
}];
|
||||||
|
|
||||||
|
let scissors = [vk::Rect2D {
|
||||||
|
offset: vk::Offset2D { x: 0, y: 0 },
|
||||||
|
extent: swapchain_extent,
|
||||||
|
}];
|
||||||
|
|
||||||
|
let viewport_state_create_info = vk::PipelineViewportStateCreateInfo {
|
||||||
|
s_type: vk::StructureType::PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: vk::PipelineViewportStateCreateFlags::empty(),
|
||||||
|
scissor_count: scissors.len() as u32,
|
||||||
|
p_scissors: scissors.as_ptr(),
|
||||||
|
viewport_count: viewports.len() as u32,
|
||||||
|
p_viewports: viewports.as_ptr(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let rasterization_statue_create_info = vk::PipelineRasterizationStateCreateInfo {
|
||||||
|
s_type: vk::StructureType::PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: vk::PipelineRasterizationStateCreateFlags::empty(),
|
||||||
|
cull_mode: vk::CullModeFlags::BACK,
|
||||||
|
front_face: vk::FrontFace::CLOCKWISE,
|
||||||
|
line_width: 1.0,
|
||||||
|
polygon_mode: vk::PolygonMode::FILL,
|
||||||
|
rasterizer_discard_enable: vk::FALSE,
|
||||||
|
depth_clamp_enable: vk::FALSE,
|
||||||
|
depth_bias_clamp: 0.0,
|
||||||
|
depth_bias_constant_factor: 0.0,
|
||||||
|
depth_bias_enable: vk::FALSE,
|
||||||
|
depth_bias_slope_factor: 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let multisample_state_create_info = vk::PipelineMultisampleStateCreateInfo {
|
||||||
|
s_type: vk::StructureType::PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||||
|
flags: vk::PipelineMultisampleStateCreateFlags::empty(),
|
||||||
|
p_next: ptr::null(),
|
||||||
|
rasterization_samples: vk::SampleCountFlags::TYPE_1,
|
||||||
|
sample_shading_enable: vk::FALSE,
|
||||||
|
min_sample_shading: 0.0,
|
||||||
|
p_sample_mask: ptr::null(),
|
||||||
|
alpha_to_one_enable: vk::FALSE,
|
||||||
|
alpha_to_coverage_enable: vk::FALSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
let stencil_state = vk::StencilOpState {
|
||||||
|
fail_op: vk::StencilOp::KEEP,
|
||||||
|
pass_op: vk::StencilOp::KEEP,
|
||||||
|
depth_fail_op: vk::StencilOp::KEEP,
|
||||||
|
compare_op: vk::CompareOp::ALWAYS,
|
||||||
|
compare_mask: 0,
|
||||||
|
write_mask: 0,
|
||||||
|
reference: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let depth_state_create_info = vk::PipelineDepthStencilStateCreateInfo {
|
||||||
|
s_type: vk::StructureType::PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: vk::PipelineDepthStencilStateCreateFlags::empty(),
|
||||||
|
depth_test_enable: vk::FALSE,
|
||||||
|
depth_write_enable: vk::FALSE,
|
||||||
|
depth_compare_op: vk::CompareOp::LESS_OR_EQUAL,
|
||||||
|
depth_bounds_test_enable: vk::FALSE,
|
||||||
|
stencil_test_enable: vk::FALSE,
|
||||||
|
front: stencil_state,
|
||||||
|
back: stencil_state,
|
||||||
|
max_depth_bounds: 1.0,
|
||||||
|
min_depth_bounds: 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let color_blend_attachment_states = [vk::PipelineColorBlendAttachmentState {
|
||||||
|
blend_enable: vk::FALSE,
|
||||||
|
color_write_mask: vk::ColorComponentFlags::all(),
|
||||||
|
src_color_blend_factor: vk::BlendFactor::ONE,
|
||||||
|
dst_color_blend_factor: vk::BlendFactor::ZERO,
|
||||||
|
color_blend_op: vk::BlendOp::ADD,
|
||||||
|
src_alpha_blend_factor: vk::BlendFactor::ONE,
|
||||||
|
dst_alpha_blend_factor: vk::BlendFactor::ZERO,
|
||||||
|
alpha_blend_op: vk::BlendOp::ADD,
|
||||||
|
}];
|
||||||
|
|
||||||
|
let color_blend_state = vk::PipelineColorBlendStateCreateInfo {
|
||||||
|
s_type: vk::StructureType::PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: vk::PipelineColorBlendStateCreateFlags::empty(),
|
||||||
|
logic_op_enable: vk::FALSE,
|
||||||
|
logic_op: vk::LogicOp::COPY,
|
||||||
|
attachment_count: color_blend_attachment_states.len() as u32,
|
||||||
|
p_attachments: color_blend_attachment_states.as_ptr(),
|
||||||
|
blend_constants: [0.0, 0.0, 0.0, 0.0],
|
||||||
|
};
|
||||||
|
|
||||||
|
let pipeline_layout_create_info = vk::PipelineLayoutCreateInfo {
|
||||||
|
s_type: vk::StructureType::PIPELINE_LAYOUT_CREATE_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: vk::PipelineLayoutCreateFlags::empty(),
|
||||||
|
set_layout_count: 0,
|
||||||
|
p_set_layouts: ptr::null(),
|
||||||
|
push_constant_range_count: 0,
|
||||||
|
p_push_constant_ranges: ptr::null(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let pipeline_layout = unsafe {
|
||||||
|
device
|
||||||
|
.create_pipeline_layout(&pipeline_layout_create_info, None)
|
||||||
|
.expect("Failed to create pipeline layout!")
|
||||||
|
};
|
||||||
|
|
||||||
|
let graphic_pipeline_create_infos = [vk::GraphicsPipelineCreateInfo {
|
||||||
|
s_type: vk::StructureType::GRAPHICS_PIPELINE_CREATE_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: vk::PipelineCreateFlags::empty(),
|
||||||
|
stage_count: shader_stages.len() as u32,
|
||||||
|
p_stages: shader_stages.as_ptr(),
|
||||||
|
p_vertex_input_state: &vertex_input_state_create_info,
|
||||||
|
p_input_assembly_state: &vertex_input_assembly_state_info,
|
||||||
|
p_tessellation_state: ptr::null(),
|
||||||
|
p_viewport_state: &viewport_state_create_info,
|
||||||
|
p_rasterization_state: &rasterization_statue_create_info,
|
||||||
|
p_multisample_state: &multisample_state_create_info,
|
||||||
|
p_depth_stencil_state: &depth_state_create_info,
|
||||||
|
p_color_blend_state: &color_blend_state,
|
||||||
|
p_dynamic_state: ptr::null(),
|
||||||
|
layout: pipeline_layout,
|
||||||
|
render_pass,
|
||||||
|
subpass: 0,
|
||||||
|
base_pipeline_handle: vk::Pipeline::null(),
|
||||||
|
base_pipeline_index: -1,
|
||||||
|
}];
|
||||||
|
|
||||||
|
let graphics_pipelines = unsafe {
|
||||||
|
device
|
||||||
|
.create_graphics_pipelines(
|
||||||
|
vk::PipelineCache::null(),
|
||||||
|
&graphic_pipeline_create_infos,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.expect("Failed to create Graphics Pipeline!.")
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
device.destroy_shader_module(vert_shader_module, None);
|
||||||
|
device.destroy_shader_module(frag_shader_module, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
(graphics_pipelines[0], pipeline_layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_frame(&mut self) {
|
||||||
|
let wait_fences = [self.in_flight_fences[self.current_frame]];
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.device
|
||||||
|
.wait_for_fences(&wait_fences, true, std::u64::MAX)
|
||||||
|
.expect("Failed to wait for Fence!");
|
||||||
|
}
|
||||||
|
|
||||||
|
let (image_index, _is_sub_optimal) = unsafe {
|
||||||
|
let result = self.swapchain_loader.acquire_next_image(
|
||||||
|
self.swapchain,
|
||||||
|
std::u64::MAX,
|
||||||
|
self.image_available_semaphores[self.current_frame],
|
||||||
|
vk::Fence::null(),
|
||||||
|
);
|
||||||
|
match result {
|
||||||
|
Ok(image_index) => image_index,
|
||||||
|
Err(vk_result) => match vk_result {
|
||||||
|
vk::Result::ERROR_OUT_OF_DATE_KHR => {
|
||||||
|
self.recreate_swapchain();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => panic!("Failed to acquire Swap Chain Image!"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let wait_semaphores = [self.image_available_semaphores[self.current_frame]];
|
||||||
|
let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT];
|
||||||
|
let signal_semaphores = [self.render_finished_semaphores[self.current_frame]];
|
||||||
|
|
||||||
|
let submit_infos = [vk::SubmitInfo {
|
||||||
|
s_type: vk::StructureType::SUBMIT_INFO,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
wait_semaphore_count: wait_semaphores.len() as u32,
|
||||||
|
p_wait_semaphores: wait_semaphores.as_ptr(),
|
||||||
|
p_wait_dst_stage_mask: wait_stages.as_ptr(),
|
||||||
|
command_buffer_count: 1,
|
||||||
|
p_command_buffers: &self.command_buffers[image_index as usize],
|
||||||
|
signal_semaphore_count: signal_semaphores.len() as u32,
|
||||||
|
p_signal_semaphores: signal_semaphores.as_ptr(),
|
||||||
|
}];
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.device
|
||||||
|
.reset_fences(&wait_fences)
|
||||||
|
.expect("Failed to reset Fence!");
|
||||||
|
|
||||||
|
self.device
|
||||||
|
.queue_submit(
|
||||||
|
self.graphics_queue,
|
||||||
|
&submit_infos,
|
||||||
|
self.in_flight_fences[self.current_frame],
|
||||||
|
)
|
||||||
|
.expect("Failed to execute queue submit.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let swapchains = [self.swapchain];
|
||||||
|
|
||||||
|
let present_info = vk::PresentInfoKHR {
|
||||||
|
s_type: vk::StructureType::PRESENT_INFO_KHR,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
wait_semaphore_count: 1,
|
||||||
|
p_wait_semaphores: signal_semaphores.as_ptr(),
|
||||||
|
swapchain_count: 1,
|
||||||
|
p_swapchains: swapchains.as_ptr(),
|
||||||
|
p_image_indices: &image_index,
|
||||||
|
p_results: ptr::null_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = unsafe {
|
||||||
|
self.swapchain_loader
|
||||||
|
.queue_present(self.present_queue, &present_info)
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_resized = match result {
|
||||||
|
Ok(_) => self.is_framebuffer_resized,
|
||||||
|
Err(vk_result) => match vk_result {
|
||||||
|
vk::Result::ERROR_OUT_OF_DATE_KHR | vk::Result::SUBOPTIMAL_KHR => true,
|
||||||
|
_ => panic!("Failed to execute queue present."),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if is_resized {
|
||||||
|
self.is_framebuffer_resized = false;
|
||||||
|
self.recreate_swapchain();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current_frame = (self.current_frame + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recreate_swapchain(&mut self) {
|
||||||
|
// parameters -------------
|
||||||
|
let surface_suff = SurfaceStuff {
|
||||||
|
surface_loader: self.surface_loader.clone(),
|
||||||
|
surface: self.surface,
|
||||||
|
screen_width: WINDOW_WIDTH,
|
||||||
|
screen_height: WINDOW_HEIGHT,
|
||||||
|
};
|
||||||
|
// ------------------------
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.device
|
||||||
|
.device_wait_idle()
|
||||||
|
.expect("Failed to wait device idle!")
|
||||||
|
};
|
||||||
|
self.cleanup_swapchain();
|
||||||
|
|
||||||
|
let swapchain_stuff = share::create_swapchain(
|
||||||
|
&self.instance,
|
||||||
|
&self.device,
|
||||||
|
self.physical_device,
|
||||||
|
&self.window,
|
||||||
|
&surface_suff,
|
||||||
|
&self.queue_family,
|
||||||
|
);
|
||||||
|
self.swapchain_loader = swapchain_stuff.swapchain_loader;
|
||||||
|
self.swapchain = swapchain_stuff.swapchain;
|
||||||
|
self.swapchain_images = swapchain_stuff.swapchain_images;
|
||||||
|
self.swapchain_format = swapchain_stuff.swapchain_format;
|
||||||
|
self.swapchain_extent = swapchain_stuff.swapchain_extent;
|
||||||
|
|
||||||
|
self.swapchain_imageviews = share::v1::create_image_views(
|
||||||
|
&self.device,
|
||||||
|
self.swapchain_format,
|
||||||
|
&self.swapchain_images,
|
||||||
|
);
|
||||||
|
self.render_pass = share::v1::create_render_pass(&self.device, self.swapchain_format);
|
||||||
|
let (graphics_pipeline, pipeline_layout) = VulkanApp::create_graphics_pipeline(
|
||||||
|
&self.device,
|
||||||
|
self.render_pass,
|
||||||
|
swapchain_stuff.swapchain_extent,
|
||||||
|
);
|
||||||
|
self.graphics_pipeline = graphics_pipeline;
|
||||||
|
self.pipeline_layout = pipeline_layout;
|
||||||
|
|
||||||
|
self.swapchain_framebuffers = share::v1::create_framebuffers(
|
||||||
|
&self.device,
|
||||||
|
self.render_pass,
|
||||||
|
&self.swapchain_imageviews,
|
||||||
|
self.swapchain_extent,
|
||||||
|
);
|
||||||
|
self.command_buffers = VulkanApp::create_command_buffers(
|
||||||
|
&self.device,
|
||||||
|
self.command_pool,
|
||||||
|
self.graphics_pipeline,
|
||||||
|
&self.swapchain_framebuffers,
|
||||||
|
self.render_pass,
|
||||||
|
self.swapchain_extent,
|
||||||
|
self.vertex_buffer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup_swapchain(&self) {
|
||||||
|
unsafe {
|
||||||
|
self.device
|
||||||
|
.free_command_buffers(self.command_pool, &self.command_buffers);
|
||||||
|
for &framebuffer in self.swapchain_framebuffers.iter() {
|
||||||
|
self.device.destroy_framebuffer(framebuffer, None);
|
||||||
|
}
|
||||||
|
self.device.destroy_pipeline(self.graphics_pipeline, None);
|
||||||
|
self.device
|
||||||
|
.destroy_pipeline_layout(self.pipeline_layout, None);
|
||||||
|
self.device.destroy_render_pass(self.render_pass, None);
|
||||||
|
for &image_view in self.swapchain_imageviews.iter() {
|
||||||
|
self.device.destroy_image_view(image_view, None);
|
||||||
|
}
|
||||||
|
self.swapchain_loader
|
||||||
|
.destroy_swapchain(self.swapchain, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for VulkanApp {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
for i in 0..MAX_FRAMES_IN_FLIGHT {
|
||||||
|
self.device
|
||||||
|
.destroy_semaphore(self.image_available_semaphores[i], None);
|
||||||
|
self.device
|
||||||
|
.destroy_semaphore(self.render_finished_semaphores[i], None);
|
||||||
|
self.device.destroy_fence(self.in_flight_fences[i], None);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cleanup_swapchain();
|
||||||
|
|
||||||
|
self.device.destroy_buffer(self.vertex_buffer, None);
|
||||||
|
self.device.free_memory(self.vertex_buffer_memory, None);
|
||||||
|
|
||||||
|
self.device.destroy_command_pool(self.command_pool, None);
|
||||||
|
|
||||||
|
self.device.destroy_device(None);
|
||||||
|
self.surface_loader.destroy_surface(self.surface, None);
|
||||||
|
|
||||||
|
if VALIDATION.is_enable {
|
||||||
|
self.debug_utils_loader
|
||||||
|
.destroy_debug_utils_messenger(self.debug_merssager, None);
|
||||||
|
}
|
||||||
|
self.instance.destroy_instance(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl VulkanApp {
|
||||||
|
|
||||||
|
pub fn main_loop(mut self, event_loop: EventLoop<()>) {
|
||||||
|
|
||||||
|
let mut tick_counter = utility::fps_limiter::FPSLimiter::new();
|
||||||
|
|
||||||
|
event_loop.run(move |event, _, control_flow| {
|
||||||
|
|
||||||
|
match event {
|
||||||
|
| Event::WindowEvent { event, .. } => {
|
||||||
|
match event {
|
||||||
|
| WindowEvent::CloseRequested => {
|
||||||
|
*control_flow = ControlFlow::Exit
|
||||||
|
},
|
||||||
|
| WindowEvent::KeyboardInput { input, .. } => {
|
||||||
|
match input {
|
||||||
|
| KeyboardInput { virtual_keycode, state, .. } => {
|
||||||
|
match (virtual_keycode, state) {
|
||||||
|
| (Some(VirtualKeyCode::Escape), ElementState::Pressed) => {
|
||||||
|
*control_flow = ControlFlow::Exit
|
||||||
|
},
|
||||||
|
| _ => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
| _ => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
| Event::MainEventsCleared => {
|
||||||
|
self.window.request_redraw();
|
||||||
|
},
|
||||||
|
| Event::RedrawRequested(_window_id) => {
|
||||||
|
self.draw_frame();
|
||||||
|
|
||||||
|
tick_counter.tick_frame();
|
||||||
|
if IS_PAINT_FPS_COUNTER {
|
||||||
|
print!("FPS: {}\r", tick_counter.fps());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
| Event::LoopDestroyed => {
|
||||||
|
unsafe {
|
||||||
|
self.device.device_wait_idle()
|
||||||
|
.expect("Failed to wait device idle!")
|
||||||
|
};
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
|
||||||
|
let event_loop = EventLoop::new();
|
||||||
|
|
||||||
|
let vulkan_app = VulkanApp::new(&event_loop);
|
||||||
|
vulkan_app.main_loop(event_loop);
|
||||||
|
}
|
||||||
|
// -------------------------------------------------------------------------------------------
|
@ -0,0 +1,2 @@
|
|||||||
|
const TRI_FRAG = include_bytes!("../target/shaders/tri.frag.spv");
|
||||||
|
const TRI_VERT = include_bytes!("../target/shaders/tri.vert.spv");
|
@ -0,0 +1,12 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
#extension GL_ARB_separate_shader_objects: enable
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 fragColor;
|
||||||
|
|
||||||
|
layout (location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
outColor = vec4(fragColor, 1.0);
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
#extension GL_ARB_separate_shader_objects: enable
|
||||||
|
|
||||||
|
layout (location = 0) in vec2 inPosition;
|
||||||
|
layout (location = 1) in vec3 inColor;
|
||||||
|
|
||||||
|
layout (location = 0) out vec3 fragColor;
|
||||||
|
|
||||||
|
out gl_PerVertex {
|
||||||
|
vec4 gl_Position;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
gl_Position = vec4(inPosition, 0.0, 1.0);
|
||||||
|
fragColor = inColor;
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
pub mod constants;
|
||||||
|
pub mod debug;
|
||||||
|
pub mod fps_limiter;
|
||||||
|
pub mod platforms;
|
||||||
|
pub mod share;
|
||||||
|
pub mod structures;
|
||||||
|
pub mod tools;
|
||||||
|
pub mod window;
|
@ -0,0 +1,30 @@
|
|||||||
|
use crate::utility::debug::ValidationInfo;
|
||||||
|
use crate::utility::structures::DeviceExtension;
|
||||||
|
use ash::vk_make_version;
|
||||||
|
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
pub const APPLICATION_VERSION: u32 = vk_make_version!(1, 0, 0);
|
||||||
|
pub const ENGINE_VERSION: u32 = vk_make_version!(1, 0, 0);
|
||||||
|
pub const API_VERSION: u32 = vk_make_version!(1, 0, 92);
|
||||||
|
|
||||||
|
pub const WINDOW_WIDTH: u32 = 800;
|
||||||
|
pub const WINDOW_HEIGHT: u32 = 600;
|
||||||
|
pub const VALIDATION: ValidationInfo = ValidationInfo {
|
||||||
|
is_enable: true,
|
||||||
|
required_validation_layers: ["VK_LAYER_KHRONOS_validation"],
|
||||||
|
};
|
||||||
|
pub const DEVICE_EXTENSIONS: DeviceExtension = DeviceExtension {
|
||||||
|
names: ["VK_KHR_swapchain"],
|
||||||
|
};
|
||||||
|
pub const MAX_FRAMES_IN_FLIGHT: usize = 2;
|
||||||
|
pub const IS_PAINT_FPS_COUNTER: bool = false;
|
||||||
|
|
||||||
|
impl DeviceExtension {
|
||||||
|
pub fn get_extensions_raw_names(&self) -> [*const c_char; 1] {
|
||||||
|
[
|
||||||
|
// currently just enable the Swapchain extension.
|
||||||
|
ash::extensions::khr::Swapchain::name().as_ptr(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
use ash::version::EntryV1_0;
|
||||||
|
use ash::vk;
|
||||||
|
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
unsafe extern "system" fn vulkan_debug_utils_callback(
|
||||||
|
message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
|
||||||
|
message_type: vk::DebugUtilsMessageTypeFlagsEXT,
|
||||||
|
p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT,
|
||||||
|
_p_user_data: *mut c_void,
|
||||||
|
) -> vk::Bool32 {
|
||||||
|
let severity = match message_severity {
|
||||||
|
vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => "[Verbose]",
|
||||||
|
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => "[Warning]",
|
||||||
|
vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => "[Error]",
|
||||||
|
vk::DebugUtilsMessageSeverityFlagsEXT::INFO => "[Info]",
|
||||||
|
_ => "[Unknown]",
|
||||||
|
};
|
||||||
|
let types = match message_type {
|
||||||
|
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL => "[General]",
|
||||||
|
vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE => "[Performance]",
|
||||||
|
vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION => "[Validation]",
|
||||||
|
_ => "[Unknown]",
|
||||||
|
};
|
||||||
|
let message = CStr::from_ptr((*p_callback_data).p_message);
|
||||||
|
println!("[Debug]{}{}{:?}", severity, types, message);
|
||||||
|
|
||||||
|
vk::FALSE
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ValidationInfo {
|
||||||
|
pub is_enable: bool,
|
||||||
|
pub required_validation_layers: [&'static str; 1],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_validation_layer_support(
|
||||||
|
entry: &ash::Entry,
|
||||||
|
required_validation_layers: &Vec<&str>,
|
||||||
|
) -> bool {
|
||||||
|
// if support validation layer, then return true
|
||||||
|
|
||||||
|
let layer_properties = entry
|
||||||
|
.enumerate_instance_layer_properties()
|
||||||
|
.expect("Failed to enumerate Instance Layers Properties");
|
||||||
|
|
||||||
|
if layer_properties.len() <= 0 {
|
||||||
|
eprintln!("No available layers.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for required_layer_name in required_validation_layers.iter() {
|
||||||
|
let mut is_layer_found = false;
|
||||||
|
|
||||||
|
for layer_property in layer_properties.iter() {
|
||||||
|
let test_layer_name = super::tools::vk_to_string(&layer_property.layer_name);
|
||||||
|
if (*required_layer_name) == test_layer_name {
|
||||||
|
is_layer_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_layer_found == false {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_debug_utils(
|
||||||
|
is_enable_debug: bool,
|
||||||
|
entry: &ash::Entry,
|
||||||
|
instance: &ash::Instance,
|
||||||
|
) -> (ash::extensions::ext::DebugUtils, vk::DebugUtilsMessengerEXT) {
|
||||||
|
let debug_utils_loader = ash::extensions::ext::DebugUtils::new(entry, instance);
|
||||||
|
|
||||||
|
if is_enable_debug == false {
|
||||||
|
(debug_utils_loader, ash::vk::DebugUtilsMessengerEXT::null())
|
||||||
|
} else {
|
||||||
|
let messenger_ci = populate_debug_messenger_create_info();
|
||||||
|
|
||||||
|
let utils_messenger = unsafe {
|
||||||
|
debug_utils_loader
|
||||||
|
.create_debug_utils_messenger(&messenger_ci, None)
|
||||||
|
.expect("Debug Utils Callback")
|
||||||
|
};
|
||||||
|
|
||||||
|
(debug_utils_loader, utils_messenger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn populate_debug_messenger_create_info() -> vk::DebugUtilsMessengerCreateInfoEXT {
|
||||||
|
vk::DebugUtilsMessengerCreateInfoEXT {
|
||||||
|
s_type: vk::StructureType::DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: vk::DebugUtilsMessengerCreateFlagsEXT::empty(),
|
||||||
|
message_severity: vk::DebugUtilsMessageSeverityFlagsEXT::WARNING |
|
||||||
|
// vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE |
|
||||||
|
// vk::DebugUtilsMessageSeverityFlagsEXT::INFO |
|
||||||
|
vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
|
||||||
|
message_type: vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
|
||||||
|
| vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE
|
||||||
|
| vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION,
|
||||||
|
pfn_user_callback: Some(vulkan_debug_utils_callback),
|
||||||
|
p_user_data: ptr::null_mut(),
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
const SAMPLE_COUNT: usize = 5;
|
||||||
|
const SAMPLE_COUNT_FLOAT: f32 = SAMPLE_COUNT as f32;
|
||||||
|
|
||||||
|
pub struct FPSLimiter {
|
||||||
|
counter: Instant,
|
||||||
|
frame_time_prefer: u32, // unit microseconds
|
||||||
|
samples: [u32; SAMPLE_COUNT],
|
||||||
|
current_frame: usize,
|
||||||
|
delta_frame: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FPSLimiter {
|
||||||
|
pub fn new() -> FPSLimiter {
|
||||||
|
const DEFAULT_PREFER_FPS: f32 = 60.0;
|
||||||
|
|
||||||
|
FPSLimiter {
|
||||||
|
counter: Instant::now(),
|
||||||
|
frame_time_prefer: (1000_000.0_f32 / DEFAULT_PREFER_FPS) as u32,
|
||||||
|
samples: [0; SAMPLE_COUNT],
|
||||||
|
current_frame: 0,
|
||||||
|
delta_frame: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_prefer_fps(&mut self, prefer_fps: f32) {
|
||||||
|
self.frame_time_prefer = (1000_000.0_f32 / prefer_fps) as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call this function in game loop to update its inner status.
|
||||||
|
pub fn tick_frame(&mut self) {
|
||||||
|
let time_elapsed = self.counter.elapsed();
|
||||||
|
self.counter = Instant::now();
|
||||||
|
|
||||||
|
self.delta_frame = time_elapsed.subsec_micros();
|
||||||
|
self.samples[self.current_frame] = self.delta_frame;
|
||||||
|
self.current_frame = (self.current_frame + 1) % SAMPLE_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this function seems not work.
|
||||||
|
pub fn keep_fps(&self) {
|
||||||
|
if self.frame_time_prefer > self.delta_frame {
|
||||||
|
let delay = Duration::from_micros((self.frame_time_prefer - self.delta_frame) as u64);
|
||||||
|
|
||||||
|
thread::sleep(delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate the current FPS.
|
||||||
|
pub fn fps(&self) -> f32 {
|
||||||
|
let mut sum = 0_u32;
|
||||||
|
self.samples.iter().for_each(|val| {
|
||||||
|
sum += val;
|
||||||
|
});
|
||||||
|
|
||||||
|
1000_000.0_f32 / (sum as f32 / SAMPLE_COUNT_FLOAT)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return current delta time in seconds
|
||||||
|
/// this function ignore its second part, since the second is mostly zero.
|
||||||
|
pub fn delta_time(&self) -> f32 {
|
||||||
|
self.delta_frame as f32 / 1000_000.0_f32 // time in second
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,134 @@
|
|||||||
|
use ash::version::{EntryV1_0, InstanceV1_0};
|
||||||
|
use ash::vk;
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
use ash::extensions::khr::Win32Surface;
|
||||||
|
#[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))]
|
||||||
|
use ash::extensions::khr::XlibSurface;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use ash::extensions::mvk::MacOSSurface;
|
||||||
|
|
||||||
|
use ash::extensions::ext::DebugUtils;
|
||||||
|
use ash::extensions::khr::Surface;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use cocoa::appkit::{NSView, NSWindow};
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use cocoa::base::id as cocoa_id;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use metal::CoreAnimationLayer;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use objc::runtime::YES;
|
||||||
|
|
||||||
|
// required extension ------------------------------------------------------
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub fn required_extension_names() -> Vec<*const i8> {
|
||||||
|
vec![
|
||||||
|
Surface::name().as_ptr(),
|
||||||
|
MacOSSurface::name().as_ptr(),
|
||||||
|
DebugUtils::name().as_ptr(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(windows))]
|
||||||
|
pub fn required_extension_names() -> Vec<*const i8> {
|
||||||
|
vec![
|
||||||
|
Surface::name().as_ptr(),
|
||||||
|
Win32Surface::name().as_ptr(),
|
||||||
|
DebugUtils::name().as_ptr(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))]
|
||||||
|
pub fn required_extension_names() -> Vec<*const i8> {
|
||||||
|
vec![
|
||||||
|
Surface::name().as_ptr(),
|
||||||
|
XlibSurface::name().as_ptr(),
|
||||||
|
DebugUtils::name().as_ptr(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// create surface ---------------------------------------------------------
|
||||||
|
#[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))]
|
||||||
|
pub unsafe fn create_surface<E: EntryV1_0, I: InstanceV1_0>(
|
||||||
|
entry: &E,
|
||||||
|
instance: &I,
|
||||||
|
window: &winit::window::Window,
|
||||||
|
) -> Result<vk::SurfaceKHR, vk::Result> {
|
||||||
|
use std::ptr;
|
||||||
|
use winit::platform::unix::WindowExtUnix;
|
||||||
|
|
||||||
|
let x11_display = window.xlib_display().unwrap();
|
||||||
|
let x11_window = window.xlib_window().unwrap();
|
||||||
|
let x11_create_info = vk::XlibSurfaceCreateInfoKHR {
|
||||||
|
s_type: vk::StructureType::XLIB_SURFACE_CREATE_INFO_KHR,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: Default::default(),
|
||||||
|
window: x11_window as vk::Window,
|
||||||
|
dpy: x11_display as *mut vk::Display,
|
||||||
|
};
|
||||||
|
let xlib_surface_loader = XlibSurface::new(entry, instance);
|
||||||
|
xlib_surface_loader.create_xlib_surface(&x11_create_info, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub unsafe fn create_surface<E: EntryV1_0, I: InstanceV1_0>(
|
||||||
|
entry: &E,
|
||||||
|
instance: &I,
|
||||||
|
window: &winit::window::Window,
|
||||||
|
) -> Result<vk::SurfaceKHR, vk::Result> {
|
||||||
|
use std::mem;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
use std::ptr;
|
||||||
|
use winit::platform::macos::WindowExtMacOS;
|
||||||
|
|
||||||
|
let wnd: cocoa_id = mem::transmute(window.ns_window());
|
||||||
|
|
||||||
|
let layer = CoreAnimationLayer::new();
|
||||||
|
|
||||||
|
layer.set_edge_antialiasing_mask(0);
|
||||||
|
layer.set_presents_with_transaction(false);
|
||||||
|
layer.remove_all_animations();
|
||||||
|
|
||||||
|
let view = wnd.contentView();
|
||||||
|
|
||||||
|
layer.set_contents_scale(view.backingScaleFactor());
|
||||||
|
view.setLayer(mem::transmute(layer.as_ref()));
|
||||||
|
view.setWantsLayer(YES);
|
||||||
|
|
||||||
|
let create_info = vk::MacOSSurfaceCreateInfoMVK {
|
||||||
|
s_type: vk::StructureType::MACOS_SURFACE_CREATE_INFO_M,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: Default::default(),
|
||||||
|
p_view: window.ns_view() as *const c_void,
|
||||||
|
};
|
||||||
|
|
||||||
|
let macos_surface_loader = MacOSSurface::new(entry, instance);
|
||||||
|
macos_surface_loader.create_mac_os_surface_mvk(&create_info, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub unsafe fn create_surface<E: EntryV1_0, I: InstanceV1_0>(
|
||||||
|
entry: &E,
|
||||||
|
instance: &I,
|
||||||
|
window: &winit::window::Window,
|
||||||
|
) -> Result<vk::SurfaceKHR, vk::Result> {
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
use std::ptr;
|
||||||
|
use winapi::shared::windef::HWND;
|
||||||
|
use winapi::um::libloaderapi::GetModuleHandleW;
|
||||||
|
use winit::platform::windows::WindowExtWindows;
|
||||||
|
|
||||||
|
let hwnd = window.hwnd() as HWND;
|
||||||
|
let hinstance = GetModuleHandleW(ptr::null()) as *const c_void;
|
||||||
|
let win32_create_info = vk::Win32SurfaceCreateInfoKHR {
|
||||||
|
s_type: vk::StructureType::WIN32_SURFACE_CREATE_INFO_KHR,
|
||||||
|
p_next: ptr::null(),
|
||||||
|
flags: Default::default(),
|
||||||
|
hinstance,
|
||||||
|
hwnd: hwnd as *const c_void,
|
||||||
|
};
|
||||||
|
let win32_surface_loader = Win32Surface::new(entry, instance);
|
||||||
|
win32_surface_loader.create_win32_surface(&win32_create_info, None)
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,155 @@
|
|||||||
|
use ash::vk;
|
||||||
|
use cgmath::Matrix4;
|
||||||
|
|
||||||
|
use memoffset::offset_of;
|
||||||
|
|
||||||
|
pub struct DeviceExtension {
|
||||||
|
pub names: [&'static str; 1],
|
||||||
|
// pub raw_names: [*const i8; 1],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SurfaceStuff {
|
||||||
|
pub surface_loader: ash::extensions::khr::Surface,
|
||||||
|
pub surface: vk::SurfaceKHR,
|
||||||
|
|
||||||
|
pub screen_width: u32,
|
||||||
|
pub screen_height: u32,
|
||||||
|
}
|
||||||
|
pub struct SwapChainStuff {
|
||||||
|
pub swapchain_loader: ash::extensions::khr::Swapchain,
|
||||||
|
pub swapchain: vk::SwapchainKHR,
|
||||||
|
pub swapchain_images: Vec<vk::Image>,
|
||||||
|
pub swapchain_format: vk::Format,
|
||||||
|
pub swapchain_extent: vk::Extent2D,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SwapChainSupportDetail {
|
||||||
|
pub capabilities: vk::SurfaceCapabilitiesKHR,
|
||||||
|
pub formats: Vec<vk::SurfaceFormatKHR>,
|
||||||
|
pub present_modes: Vec<vk::PresentModeKHR>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct QueueFamilyIndices {
|
||||||
|
pub graphics_family: Option<u32>,
|
||||||
|
pub present_family: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QueueFamilyIndices {
|
||||||
|
pub fn new() -> QueueFamilyIndices {
|
||||||
|
QueueFamilyIndices {
|
||||||
|
graphics_family: None,
|
||||||
|
present_family: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_complete(&self) -> bool {
|
||||||
|
self.graphics_family.is_some() && self.present_family.is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SyncObjects {
|
||||||
|
pub image_available_semaphores: Vec<vk::Semaphore>,
|
||||||
|
pub render_finished_semaphores: Vec<vk::Semaphore>,
|
||||||
|
pub inflight_fences: Vec<vk::Fence>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Debug, Copy)]
|
||||||
|
pub struct UniformBufferObject {
|
||||||
|
pub model: Matrix4<f32>,
|
||||||
|
pub view: Matrix4<f32>,
|
||||||
|
pub proj: Matrix4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Debug, Copy)]
|
||||||
|
pub struct VertexV1 {
|
||||||
|
pub pos: [f32; 2],
|
||||||
|
pub color: [f32; 3],
|
||||||
|
}
|
||||||
|
impl VertexV1 {
|
||||||
|
pub fn get_binding_description() -> [vk::VertexInputBindingDescription; 1] {
|
||||||
|
[vk::VertexInputBindingDescription {
|
||||||
|
binding: 0,
|
||||||
|
stride: ::std::mem::size_of::<VertexV1>() as u32,
|
||||||
|
input_rate: vk::VertexInputRate::VERTEX,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_attribute_descriptions() -> [vk::VertexInputAttributeDescription; 2] {
|
||||||
|
[
|
||||||
|
vk::VertexInputAttributeDescription {
|
||||||
|
binding: 0,
|
||||||
|
location: 0,
|
||||||
|
format: vk::Format::R32G32_SFLOAT,
|
||||||
|
offset: offset_of!(VertexV1, pos) as u32,
|
||||||
|
},
|
||||||
|
vk::VertexInputAttributeDescription {
|
||||||
|
binding: 0,
|
||||||
|
location: 1,
|
||||||
|
format: vk::Format::R32G32B32_SFLOAT,
|
||||||
|
offset: offset_of!(VertexV1, color) as u32,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct VertexV3 {
|
||||||
|
pub pos: [f32; 4],
|
||||||
|
pub color: [f32; 4],
|
||||||
|
pub tex_coord: [f32; 2],
|
||||||
|
}
|
||||||
|
impl VertexV3 {
|
||||||
|
pub fn get_binding_descriptions() -> [vk::VertexInputBindingDescription; 1] {
|
||||||
|
[vk::VertexInputBindingDescription {
|
||||||
|
binding: 0,
|
||||||
|
stride: ::std::mem::size_of::<Self>() as u32,
|
||||||
|
input_rate: vk::VertexInputRate::VERTEX,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_attribute_descriptions() -> [vk::VertexInputAttributeDescription; 3] {
|
||||||
|
[
|
||||||
|
vk::VertexInputAttributeDescription {
|
||||||
|
binding: 0,
|
||||||
|
location: 0,
|
||||||
|
format: vk::Format::R32G32B32A32_SFLOAT,
|
||||||
|
offset: offset_of!(Self, pos) as u32,
|
||||||
|
},
|
||||||
|
vk::VertexInputAttributeDescription {
|
||||||
|
binding: 0,
|
||||||
|
location: 1,
|
||||||
|
format: vk::Format::R32G32B32A32_SFLOAT,
|
||||||
|
offset: offset_of!(Self, color) as u32,
|
||||||
|
},
|
||||||
|
vk::VertexInputAttributeDescription {
|
||||||
|
binding: 0,
|
||||||
|
location: 2,
|
||||||
|
format: vk::Format::R32G32_SFLOAT,
|
||||||
|
offset: offset_of!(Self, tex_coord) as u32,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const RECT_VERTICES_DATA: [VertexV1; 4] = [
|
||||||
|
VertexV1 {
|
||||||
|
pos: [-0.5, -0.5],
|
||||||
|
color: [1.0, 0.0, 0.0],
|
||||||
|
},
|
||||||
|
VertexV1 {
|
||||||
|
pos: [0.5, -0.5],
|
||||||
|
color: [0.0, 1.0, 0.0],
|
||||||
|
},
|
||||||
|
VertexV1 {
|
||||||
|
pos: [0.5, 0.5],
|
||||||
|
color: [0.0, 0.0, 1.0],
|
||||||
|
},
|
||||||
|
VertexV1 {
|
||||||
|
pos: [-0.5, 0.5],
|
||||||
|
color: [1.0, 1.0, 1.0],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
pub const RECT_INDICES_DATA: [u32; 6] = [0, 1, 2, 2, 3, 0];
|
@ -0,0 +1,46 @@
|
|||||||
|
use std::ffi::CStr;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
/// Helper function to convert [c_char; SIZE] to string
|
||||||
|
pub fn vk_to_string(raw_string_array: &[c_char]) -> String {
|
||||||
|
// Implementation 1
|
||||||
|
// let end = '\0' as u8;
|
||||||
|
//
|
||||||
|
// let mut content: Vec<u8> = vec![];
|
||||||
|
//
|
||||||
|
// for ch in raw_string_array.iter() {
|
||||||
|
// let ch = (*ch) as u8;
|
||||||
|
//
|
||||||
|
// if ch != end {
|
||||||
|
// content.push(ch);
|
||||||
|
// } else {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// String::from_utf8(content)
|
||||||
|
// .expect("Failed to convert vulkan raw string")
|
||||||
|
|
||||||
|
// Implementation 2
|
||||||
|
let raw_string = unsafe {
|
||||||
|
let pointer = raw_string_array.as_ptr();
|
||||||
|
CStr::from_ptr(pointer)
|
||||||
|
};
|
||||||
|
|
||||||
|
raw_string
|
||||||
|
.to_str()
|
||||||
|
.expect("Failed to convert vulkan raw string.")
|
||||||
|
.to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_shader_code(shader_path: &Path) -> Vec<u8> {
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
let spv_file =
|
||||||
|
File::open(shader_path).expect(&format!("Failed to find spv file at {:?}", shader_path));
|
||||||
|
let bytes_code: Vec<u8> = spv_file.bytes().filter_map(|byte| byte.ok()).collect();
|
||||||
|
|
||||||
|
bytes_code
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent};
|
||||||
|
use winit::event_loop::{EventLoop, ControlFlow};
|
||||||
|
|
||||||
|
|
||||||
|
const IS_PAINT_FPS_COUNTER: bool = true;
|
||||||
|
|
||||||
|
pub fn init_window(
|
||||||
|
event_loop: &EventLoop<()>,
|
||||||
|
title: &str,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) -> winit::window::Window {
|
||||||
|
winit::window::WindowBuilder::new()
|
||||||
|
.with_title(title)
|
||||||
|
.with_inner_size(winit::dpi::LogicalSize::new(width, height))
|
||||||
|
.build(event_loop)
|
||||||
|
.expect("Failed to create window.")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait VulkanApp {
|
||||||
|
fn draw_frame(&mut self, delta_time: f32);
|
||||||
|
fn recreate_swapchain(&mut self);
|
||||||
|
fn cleanup_swapchain(&self);
|
||||||
|
fn wait_device_idle(&self);
|
||||||
|
fn resize_framebuffer(&mut self);
|
||||||
|
fn window_ref(&self) -> &winit::window::Window;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ProgramProc {
|
||||||
|
pub event_loop: EventLoop<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProgramProc {
|
||||||
|
|
||||||
|
pub fn new() -> ProgramProc {
|
||||||
|
// init window stuff
|
||||||
|
let event_loop = EventLoop::new();
|
||||||
|
|
||||||
|
ProgramProc { event_loop }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_loop<A: 'static + VulkanApp>(self, mut vulkan_app: A) {
|
||||||
|
|
||||||
|
let mut tick_counter = super::fps_limiter::FPSLimiter::new();
|
||||||
|
|
||||||
|
self.event_loop.run(move |event, _, control_flow| {
|
||||||
|
|
||||||
|
match event {
|
||||||
|
| Event::WindowEvent { event, .. } => {
|
||||||
|
match event {
|
||||||
|
| WindowEvent::CloseRequested => {
|
||||||
|
vulkan_app.wait_device_idle();
|
||||||
|
*control_flow = ControlFlow::Exit
|
||||||
|
},
|
||||||
|
| WindowEvent::KeyboardInput { input, .. } => {
|
||||||
|
match input {
|
||||||
|
| KeyboardInput { virtual_keycode, state, .. } => {
|
||||||
|
match (virtual_keycode, state) {
|
||||||
|
| (Some(VirtualKeyCode::Escape), ElementState::Pressed) => {
|
||||||
|
vulkan_app.wait_device_idle();
|
||||||
|
*control_flow = ControlFlow::Exit
|
||||||
|
},
|
||||||
|
| _ => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
| WindowEvent::Resized(_new_size) => {
|
||||||
|
vulkan_app.wait_device_idle();
|
||||||
|
vulkan_app.resize_framebuffer();
|
||||||
|
},
|
||||||
|
| _ => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
| Event::MainEventsCleared => {
|
||||||
|
vulkan_app.window_ref().request_redraw();
|
||||||
|
},
|
||||||
|
| Event::RedrawRequested(_window_id) => {
|
||||||
|
let delta_time = tick_counter.delta_time();
|
||||||
|
vulkan_app.draw_frame(delta_time);
|
||||||
|
|
||||||
|
if IS_PAINT_FPS_COUNTER {
|
||||||
|
print!("FPS: {}\r", tick_counter.fps());
|
||||||
|
}
|
||||||
|
|
||||||
|
tick_counter.tick_frame();
|
||||||
|
},
|
||||||
|
| Event::LoopDestroyed => {
|
||||||
|
vulkan_app.wait_device_idle();
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in new issue