diff --git a/librashader-runtime-d3d11/src/lib.rs b/librashader-runtime-d3d11/src/lib.rs index d877fed..2685a3d 100644 --- a/librashader-runtime-d3d11/src/lib.rs +++ b/librashader-runtime-d3d11/src/lib.rs @@ -32,7 +32,7 @@ mod tests { None, ) .unwrap(); - // let sample = hello_triangle::d3d11_hello_triangle::Sample::new( + // let sample = hello_triangle_old::d3d11_hello_triangle::Sample::new( // "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", // Some(&FilterChainOptions { // use_deferred_context: true, @@ -40,7 +40,7 @@ mod tests { // ) // .unwrap(); - // let sample = hello_triangle::d3d11_hello_triangle::Sample::new("../test/basic.slangp").unwrap(); + // let sample = hello_triangle_old::d3d11_hello_triangle::Sample::new("../test/basic.slangp").unwrap(); hello_triangle::main(sample).unwrap(); } diff --git a/librashader-runtime-vk/src/hello_triangle/debug.rs b/librashader-runtime-vk/src/hello_triangle/debug.rs new file mode 100644 index 0000000..93a4d8e --- /dev/null +++ b/librashader-runtime-vk/src/hello_triangle/debug.rs @@ -0,0 +1,46 @@ +use ash::extensions::ext::DebugUtils; +use ash::prelude::VkResult; +use ash::vk; +use ash::vk::{DebugUtilsMessengerEXT, PFN_vkDebugUtilsMessengerCallbackEXT}; + +pub struct VulkanDebug { + loader: DebugUtils, + messenger: DebugUtilsMessengerEXT +} + +impl VulkanDebug { + pub fn new(entry: &ash::Entry, instance: &ash::Instance, callback: PFN_vkDebugUtilsMessengerCallbackEXT) -> VkResult{ + let debug_info = vk::DebugUtilsMessengerCreateInfoEXT::builder() + .message_severity( + vk::DebugUtilsMessageSeverityFlagsEXT::ERROR + | vk::DebugUtilsMessageSeverityFlagsEXT::WARNING + | vk::DebugUtilsMessageSeverityFlagsEXT::INFO, + ) + .message_type( + vk::DebugUtilsMessageTypeFlagsEXT::GENERAL + | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION + | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE, + ) + .pfn_user_callback(callback); + + let debug_utils_loader = DebugUtils::new(entry, instance); + + unsafe { + let debug_call_back = debug_utils_loader + .create_debug_utils_messenger(&debug_info, None)?; + + Ok(VulkanDebug { + loader: debug_utils_loader, + messenger: debug_call_back + }) + } + } +} + +impl Drop for VulkanDebug { + fn drop(&mut self) { + unsafe { + self.loader.destroy_debug_utils_messenger(self.messenger, None); + } + } +} \ No newline at end of file diff --git a/librashader-runtime-vk/src/hello_triangle/mod.rs b/librashader-runtime-vk/src/hello_triangle/mod.rs new file mode 100644 index 0000000..9dc234a --- /dev/null +++ b/librashader-runtime-vk/src/hello_triangle/mod.rs @@ -0,0 +1,81 @@ +pub mod vulkan_base; +mod debug; +mod physicaldevice; +mod surface; + +use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; +use winit::event_loop::{EventLoop, ControlFlow, EventLoopBuilder}; +use winit::platform::windows::EventLoopBuilderExtWindows; +use crate::filter_chain::FilterChainVulkan; +use crate::hello_triangle::surface::VulkanSurface; +use crate::hello_triangle::vulkan_base::VulkanBase; + +// Constants +const WINDOW_TITLE: &'static str = "librashader Vulkan"; +const WINDOW_WIDTH: u32 = 800; +const WINDOW_HEIGHT: u32 = 600; + +struct VulkanWindow; + +impl VulkanWindow { + fn init_window(event_loop: &EventLoop<()>) -> winit::window::Window { + winit::window::WindowBuilder::new() + .with_title(WINDOW_TITLE) + .with_inner_size(winit::dpi::LogicalSize::new(WINDOW_WIDTH, WINDOW_HEIGHT)) + .build(event_loop) + .expect("Failed to create window.") + } + + pub fn main_loop(event_loop: EventLoop<()>) { + 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) => { + dbg!(); + *control_flow = ControlFlow::Exit + }, + | _ => {}, + } + }, + } + }, + | _ => {}, + } + }, + _ => (), + } + + }) + } +} + +pub struct VulkanDraw { + surface: VulkanSurface, + base: VulkanBase, +} + +pub fn main(vulkan: VulkanBase, filter_chain: FilterChainVulkan) { + let event_loop = EventLoopBuilder::new() + .with_any_thread(true) + .with_dpi_aware(true) + .build(); + + let window = VulkanWindow::init_window(&event_loop); + let surface = VulkanSurface::new(&vulkan, &window) + .unwrap(); + + let vulkan = VulkanDraw { + surface, + base: vulkan + }; + + VulkanWindow::main_loop(event_loop); +} \ No newline at end of file diff --git a/librashader-runtime-vk/src/hello_triangle/physicaldevice.rs b/librashader-runtime-vk/src/hello_triangle/physicaldevice.rs new file mode 100644 index 0000000..7898b37 --- /dev/null +++ b/librashader-runtime-vk/src/hello_triangle/physicaldevice.rs @@ -0,0 +1,151 @@ +use std::ffi::CStr; +use ash::vk; + +pub struct QueueFamilyIndices { + graphics_family: Option, +} + +impl QueueFamilyIndices { + pub fn is_complete(&self) -> bool { + self.graphics_family.is_some() + } + + pub fn graphics_family(&self) -> u32 { + self.graphics_family.unwrap() + } +} + +pub(crate) fn pick_physical_device(instance: &ash::Instance) -> vk::PhysicalDevice { + let physical_devices = unsafe { + instance + .enumerate_physical_devices() + .expect("Failed to enumerate Physical Devices!") + }; + + let mut result = None; + for &physical_device in physical_devices.iter() { + if is_physical_device_suitable(instance, physical_device) { + if result.is_none() { + result = Some(physical_device) + } + } + } + + match result { + None => panic!("Failed to find a suitable GPU!"), + Some(physical_device) => physical_device, + } +} + +fn is_physical_device_suitable( + instance: &ash::Instance, + physical_device: vk::PhysicalDevice, +) -> bool { + let device_properties = unsafe { instance.get_physical_device_properties(physical_device) }; + let device_features = unsafe { instance.get_physical_device_features(physical_device) }; + let device_queue_families = + unsafe { instance.get_physical_device_queue_family_properties(physical_device) }; + + let device_type = match device_properties.device_type { + vk::PhysicalDeviceType::CPU => "Cpu", + vk::PhysicalDeviceType::INTEGRATED_GPU => "Integrated GPU", + vk::PhysicalDeviceType::DISCRETE_GPU => "Discrete GPU", + vk::PhysicalDeviceType::VIRTUAL_GPU => "Virtual GPU", + vk::PhysicalDeviceType::OTHER => "Unknown", + _ => panic!(), + }; + + let device_name = unsafe { CStr::from_ptr(device_properties.device_name.as_ptr()) }; + let device_name = device_name.to_string_lossy(); + println!( + "\tDevice Name: {}, id: {}, type: {}", + device_name, device_properties.device_id, device_type + ); + + println!( + "\tAPI Version: {}", + device_properties.api_version + ); + + println!("\tSupport Queue Family: {}", device_queue_families.len()); + println!("\t\tQueue Count | Graphics, Compute, Transfer, Sparse Binding"); + for queue_family in device_queue_families.iter() { + let is_graphics_support = if queue_family.queue_flags.contains(vk::QueueFlags::GRAPHICS) + { + "support" + } else { + "unsupport" + }; + let is_compute_support = if queue_family.queue_flags.contains(vk::QueueFlags::COMPUTE) { + "support" + } else { + "unsupport" + }; + let is_transfer_support = if queue_family.queue_flags.contains(vk::QueueFlags::TRANSFER) + { + "support" + } else { + "unsupport" + }; + let is_sparse_support = if queue_family + .queue_flags + .contains(vk::QueueFlags::SPARSE_BINDING) + { + "support" + } else { + "unsupport" + }; + + println!( + "\t\t{}\t | {}, {}, {}, {}", + queue_family.queue_count, + is_graphics_support, + is_compute_support, + is_transfer_support, + is_sparse_support + ); + } + + // there are plenty of features + println!( + "\tGeometry Shader support: {}", + if device_features.geometry_shader == 1 { + "support" + } else { + "unsupported" + } + ); + + let indices = find_queue_family(instance, physical_device); + + return indices.is_complete(); +} + +pub fn find_queue_family( + instance: &ash::Instance, + physical_device: vk::PhysicalDevice, +) -> QueueFamilyIndices { + let queue_families = + unsafe { instance.get_physical_device_queue_family_properties(physical_device) }; + + let mut queue_family_indices = QueueFamilyIndices { + graphics_family: None, + }; + + let mut index = 0; + for queue_family in queue_families.iter() { + if queue_family.queue_count > 0 + && queue_family.queue_flags.contains(vk::QueueFlags::GRAPHICS) + { + queue_family_indices.graphics_family = Some(index); + } + + if queue_family_indices.is_complete() { + break; + } + + index += 1; + } + + queue_family_indices +} \ No newline at end of file diff --git a/librashader-runtime-vk/src/hello_triangle/surface.rs b/librashader-runtime-vk/src/hello_triangle/surface.rs new file mode 100644 index 0000000..02c9627 --- /dev/null +++ b/librashader-runtime-vk/src/hello_triangle/surface.rs @@ -0,0 +1,60 @@ +use ash::prelude::VkResult; +use ash::vk; +use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; +use crate::hello_triangle::vulkan_base::VulkanBase; + +pub struct VulkanSurface { + surface_loader: ash::extensions::khr::Surface, + surface: vk::SurfaceKHR, + present_queue: vk::Queue +} + +impl VulkanSurface { + pub fn new(base: &VulkanBase, + window: &winit::window::Window) -> VkResult + { + let surface = unsafe { + ash_window::create_surface( + &base.entry, + &base.instance, + window.raw_display_handle(), + window.raw_window_handle(), + None, + )? + }; + + let surface_loader = ash::extensions::khr::Surface::new(&base.entry, &base.instance); + + let present_queue = unsafe { + let queue_family = base.instance + .get_physical_device_queue_family_properties(base.physical_device) + .iter() + .enumerate() + .find_map(|(index, info)| { + let supports_graphic_and_surface = + info.queue_flags.contains(vk::QueueFlags::GRAPHICS) + && surface_loader + .get_physical_device_surface_support( + base.physical_device, + index as u32, + surface, + ) + .unwrap(); + if supports_graphic_and_surface { + Some(index) + } else { + None + } + }) + .expect("couldn't find suitable device"); + base.device.get_device_queue(queue_family as u32, 0) + }; + + Ok(VulkanSurface { + surface, + surface_loader, + present_queue + }) + } +} + diff --git a/librashader-runtime-vk/src/hello_triangle/vulkan_base.rs b/librashader-runtime-vk/src/hello_triangle/vulkan_base.rs new file mode 100644 index 0000000..06fdb7e --- /dev/null +++ b/librashader-runtime-vk/src/hello_triangle/vulkan_base.rs @@ -0,0 +1,165 @@ +use std::borrow::Cow; +use std::error::Error; +use ash::vk; + +use std::ffi::{CStr, CString}; +use ash::prelude::VkResult; +use crate::filter_chain::Vulkan; +use crate::hello_triangle::debug::VulkanDebug; +use crate::hello_triangle::physicaldevice::{find_queue_family, pick_physical_device}; +use crate::hello_triangle::surface::VulkanSurface; + + +const WINDOW_TITLE: &'static str = "librashader Vulkan"; + +pub struct VulkanBase { + pub entry: ash::Entry, + pub instance: ash::Instance, + pub device: ash::Device, + pub graphics_queue: vk::Queue, + pub debug: VulkanDebug, + pub physical_device: vk::PhysicalDevice, + pub mem_props: vk::PhysicalDeviceMemoryProperties, +} + +impl VulkanBase { + pub fn new(entry: ash::Entry) -> VkResult { + let app_name = CString::new(WINDOW_TITLE).unwrap(); + let engine_name = CString::new("librashader").unwrap(); + + let app_info = vk::ApplicationInfo::builder() + .application_name(&app_name) + .engine_name(&engine_name) + .engine_version(0) + .application_version(0) + .api_version(vk::make_api_version(0, 1, 3, 0)) + .build(); + + // todo: make this xplat + let extensions = [ + ash::extensions::khr::Surface::name().as_ptr(), + ash::extensions::khr::Win32Surface::name().as_ptr(), + ash::extensions::ext::DebugUtils::name().as_ptr() + ]; + + let layers = unsafe { + [CStr::from_bytes_with_nul_unchecked( + b"VK_LAYER_KHRONOS_validation\0", + ).as_ptr()] + }; + + let create_info = vk::InstanceCreateInfo::builder() + .application_info(&app_info) + .enabled_layer_names(&layers) + .enabled_extension_names(&extensions) + .build(); + + let instance = unsafe { + entry.create_instance(&create_info, None)? + }; + + let debug = VulkanDebug::new(&entry, &instance, Some(vulkan_debug_callback))?; + + let physical_device = pick_physical_device(&instance); + + let (device, queue) = VulkanBase::create_device(&instance, &physical_device)?; + + let mem_props = unsafe { + instance.get_physical_device_memory_properties(physical_device) + }; + + Ok(VulkanBase { + entry, + instance, + device, + graphics_queue: queue, + physical_device, + mem_props, + debug, + }) + } + + fn create_device(instance: &ash::Instance, physical_device: &vk::PhysicalDevice) -> VkResult<(ash::Device, vk::Queue)> { + let debug = unsafe { + CStr::from_bytes_with_nul_unchecked( + b"VK_LAYER_KHRONOS_validation\0", + ).as_ptr() + }; + + let indices = find_queue_family(&instance, *physical_device); + let queue_info = vk::DeviceQueueCreateInfo::builder() + .queue_family_index(indices.graphics_family()) + .queue_priorities(&[1.0f32]) + .build(); + + let physical_device_features = vk::PhysicalDeviceFeatures::default(); + + let device_create_info = vk::DeviceCreateInfo::builder() + .queue_create_infos(&[queue_info]) + .enabled_layer_names(&[debug]) + .enabled_extension_names(&[ + ash::extensions::khr::Swapchain::name().as_ptr() + ]) + .enabled_features(&physical_device_features) + .build(); + + let device = unsafe { + instance.create_device(*physical_device, &device_create_info, None)? + }; + + let queue = unsafe { device.get_device_queue(indices.graphics_family(), 0) }; + + Ok((device, queue)) + } +} + + +unsafe extern "system" fn vulkan_debug_callback( + message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, + message_type: vk::DebugUtilsMessageTypeFlagsEXT, + p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT, + _user_data: *mut std::os::raw::c_void, +) -> vk::Bool32 { + let callback_data = *p_callback_data; + let message_id_number: i32 = callback_data.message_id_number as i32; + + let message_id_name = if callback_data.p_message_id_name.is_null() { + Cow::from("") + } else { + CStr::from_ptr(callback_data.p_message_id_name).to_string_lossy() + }; + + let message = if callback_data.p_message.is_null() { + Cow::from("") + } else { + CStr::from_ptr(callback_data.p_message).to_string_lossy() + }; + + println!( + "{:?}:\n{:?} [{} ({})] : {}\n", + message_severity, + message_type, + message_id_name, + &message_id_number.to_string(), + message, + ); + + vk::FALSE +} + +impl Drop for VulkanBase { + fn drop(&mut self) { + unsafe { + self.device.destroy_device(None); + self.instance.destroy_instance(None); + } + } +} + +impl TryFrom<&VulkanBase> for Vulkan { + type Error = Box; + + fn try_from(value: &VulkanBase) -> Result { + Vulkan::try_from((value.device.clone(), value.graphics_queue.clone(), value.mem_props)) + } +} \ No newline at end of file diff --git a/librashader-runtime-vk/src/hello_triangle/base.rs b/librashader-runtime-vk/src/hello_triangle_old/base.rs similarity index 100% rename from librashader-runtime-vk/src/hello_triangle/base.rs rename to librashader-runtime-vk/src/hello_triangle_old/base.rs diff --git a/librashader-runtime-vk/src/hello_triangle.rs b/librashader-runtime-vk/src/hello_triangle_old/mod.rs similarity index 99% rename from librashader-runtime-vk/src/hello_triangle.rs rename to librashader-runtime-vk/src/hello_triangle_old/mod.rs index ce16f9d..c8ec321 100644 --- a/librashader-runtime-vk/src/hello_triangle.rs +++ b/librashader-runtime-vk/src/hello_triangle_old/mod.rs @@ -203,8 +203,8 @@ pub(crate) fn main(base: ExampleBase, mut filter_chain: FilterChainVulkan) { base.device .bind_buffer_memory(vertex_input_buffer, vertex_input_buffer_memory, 0) .unwrap(); - let mut vertex_spv_file = Cursor::new(&include_bytes!("../shader/triangle/vert.spv")[..]); - let mut frag_spv_file = Cursor::new(&include_bytes!("../shader/triangle/frag.spv")[..]); + let mut vertex_spv_file = Cursor::new(&include_bytes!("../../shader/triangle/vert.spv")[..]); + let mut frag_spv_file = Cursor::new(&include_bytes!("../../shader/triangle/frag.spv")[..]); let vertex_code = read_spv(&mut vertex_spv_file).expect("Failed to read vertex shader spv file"); diff --git a/librashader-runtime-vk/src/lib.rs b/librashader-runtime-vk/src/lib.rs index d03325f..6ccaf51 100644 --- a/librashader-runtime-vk/src/lib.rs +++ b/librashader-runtime-vk/src/lib.rs @@ -7,7 +7,7 @@ mod error; mod filter_chain; mod filter_pass; mod framebuffer; -mod hello_triangle; +mod hello_triangle_old; mod luts; mod renderpass; mod util; @@ -17,31 +17,46 @@ mod samplers; mod texture; mod rendertarget; mod ubo_ring; +mod hello_triangle; #[cfg(test)] mod tests { use super::*; - use crate::filter_chain::FilterChainVulkan; + use crate::filter_chain::{FilterChainVulkan, Vulkan}; + use crate::hello_triangle::vulkan_base::VulkanBase; + #[test] fn triangle_vk() { - let base = hello_triangle::ExampleBase::new(900, 600); - // let mut filter = FilterChainVulkan::load_from_path( - // (base.device.clone(), base.present_queue.clone(), base.device_memory_properties.clone()), - // "../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp", - // None - // ) - + let entry = unsafe { + ash::Entry::load().unwrap() + }; + let base = VulkanBase::new(entry).unwrap(); let mut filter = FilterChainVulkan::load_from_path( - ( - base.device.clone(), - base.present_queue.clone(), - base.device_memory_properties.clone(), - ), + &base, "../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp", - None, - ) - // FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None) - .unwrap(); - hello_triangle::main(base, filter); + None + ).unwrap(); + + crate::hello_triangle::main(base, filter) + + // let base = hello_triangle_old::ExampleBase::new(900, 600); + // // let mut filter = FilterChainVulkan::load_from_path( + // // (base.device.clone(), base.present_queue.clone(), base.device_memory_properties.clone()), + // // "../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp", + // // None + // // ) + // + // let mut filter = FilterChainVulkan::load_from_path( + // ( + // base.device.clone(), + // base.present_queue.clone(), + // base.device_memory_properties.clone(), + // ), + // "../test/slang-shaders/border/gameboy-player/gameboy-player-crt-royale.slangp", + // None, + // ) + // // FilterChain::load_from_path("../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", None) + // .unwrap(); + // hello_triangle_old::main(base, filter); } }