use std::ffi::{c_char, CString}; use ash::vk; use buffer::{Buffer, BufferAllocator}; pub mod buffer; pub mod debug; pub mod error; pub mod pipeline; pub mod pools; pub mod queue; pub mod surface; pub mod swapchain; pub mod window; pub use error::EngineError; pub use debug::DebugInstance; pub use pipeline::Pipeline; use pools::Pools; pub use queue::{Queue, QueueFamilies, Queues}; pub use surface::Surface; pub use swapchain::Swapchain; pub use window::Window; use crate::frame_counter::FrameCounter; const ENGINE_NAME: &core::ffi::CStr = c"UnknownGameEngine"; const APP_NAME: &core::ffi::CStr = c"Vulkan Tutorial"; pub struct EngineInstance { pub entry: ash::Entry, pub ash_instance: ash::Instance, pub debug: std::mem::ManuallyDrop, pub surface: std::mem::ManuallyDrop, pub surface_format: vk::SurfaceFormatKHR, pub physical_device: vk::PhysicalDevice, pub physical_device_properties: vk::PhysicalDeviceProperties, pub physical_device_memory_properties: vk::PhysicalDeviceMemoryProperties, pub device: ash::Device, pub queues: Queues, pub swapchain: Swapchain, pub renderpass: vk::RenderPass, pub pipeline: Pipeline, pub pools: Pools, pub commandbuffers: Vec, pub allocator: std::mem::ManuallyDrop, pub buffers: Vec, pub framecount: FrameCounter, } impl EngineInstance { pub fn init(window: &Window) -> Result { log::debug!("Init window"); let entry = unsafe { ash::Entry::load()? }; let layers = Self::layer_names(); let layers_pointers = as_pointers(&layers); // create ash instance log::debug!("Init instance"); let extensions = window.extension_names()?; let ash_instance = Self::init_instance(&entry, &layers_pointers, &extensions)?; log::debug!("Init debugger"); let debug = DebugInstance::init(&entry, &ash_instance)?; log::debug!("Init surface"); let surface = Surface::init(&entry, &ash_instance, &window)?; // let (surface, surface_loader) = Self::init_surface(&entry, &ash_instance, &window)?; log::debug!("Init device"); let (physical_device, physical_device_properties) = Self::select_physical_device(&ash_instance)?; let physical_device_memory_properties = unsafe { ash_instance.get_physical_device_memory_properties(physical_device) }; dbg!(&physical_device_properties); dbg!(&physical_device_memory_properties); log::debug!("Select surface properties"); let surface_capabilities = surface.get_capabilities(physical_device)?; let surface_formats = surface.get_formats(physical_device)?; let surface_resolution = Surface::select_resolution(&surface_capabilities, &window.res); let surface_format = Surface::select_format(&surface_formats); log::debug!("Select queues"); let queue_families = QueueFamilies::init(&ash_instance, physical_device, &surface)?; let queueinfo = queue_families.make_creation_info()?; log::debug!("Create device"); let device = Self::create_device(&ash_instance, physical_device, &layers_pointers, &queueinfo)?; log::debug!("Create queues"); let queues = queue_families.create_queues(&device)?; // create swapchain log::debug!("Create swapchain"); let mut swapchain = Swapchain::init( &ash_instance, &device, &surface, &surface_capabilities, surface_format, surface_resolution, &queues.graphics_queue.as_ref().unwrap(), )?; log::debug!("Create framebuffers"); let renderpass = Self::init_renderpass(&device, surface_format.format)?; swapchain.create_framebuffers(&device, renderpass)?; log::debug!("Create allocator"); let mut allocator = BufferAllocator::init(&ash_instance, physical_device, &device)?; let mut buffer1 = allocator.create_buffer(&device, 16)?; unsafe { let data: [u8; 16] = std::mem::transmute([-0.5f32, 0.0f32, 0.0f32, 1.0f32]); buffer1.fill(&data); } let mut buffer2 = allocator.create_buffer(&device, 20)?; unsafe { let data: [u8; 20] = std::mem::transmute([5.0f32, 1.0f32, 1.0f32, 0.0f32, 1.0f32]); buffer2.fill(&data); } let vertex_attrib_descs = [ vk::VertexInputAttributeDescription { binding: 0, location: 0, offset: 0, format: vk::Format::R32G32B32A32_SFLOAT, }, vk::VertexInputAttributeDescription { binding: 1, location: 1, offset: 0, format: vk::Format::R32_SFLOAT, }, vk::VertexInputAttributeDescription { binding: 1, location: 2, offset: 4, format: vk::Format::R32G32B32A32_SFLOAT, }, ]; let vertex_binding_descs = [ vk::VertexInputBindingDescription { binding: 0, stride: 16, input_rate: vk::VertexInputRate::VERTEX, }, vk::VertexInputBindingDescription { binding: 1, stride: 20, input_rate: vk::VertexInputRate::VERTEX, }, ]; log::debug!("Init pipeline"); let pipeline = Pipeline::init( &device, &swapchain, &renderpass, &vertex_attrib_descs, &vertex_binding_descs, )?; log::debug!("Init pools"); let pools = Pools::init(&device, &queue_families)?; log::debug!("Create command buffers"); let commandbuffers = pools.create_commandbuffers(&device, swapchain.framebuffers.len())?; for (i, &commandbuffer) in commandbuffers.iter().enumerate() { Self::fill_commandbuffer( &device, commandbuffer, renderpass, &swapchain, i, &pipeline, buffer1.buffer, buffer2.buffer, )?; } Ok(Self { entry, ash_instance, surface: std::mem::ManuallyDrop::new(surface), surface_format: *surface_format, debug: std::mem::ManuallyDrop::new(debug), physical_device, physical_device_properties, physical_device_memory_properties, device, queues, swapchain, renderpass, pipeline, pools, commandbuffers, allocator: std::mem::ManuallyDrop::new(allocator), buffers: vec![buffer1, buffer2], framecount: FrameCounter::new(), }) } fn destroy(&mut self) { unsafe { self.device .device_wait_idle() .expect("something wrong while waiting"); self.pools.cleanup(&self.device); self.pipeline.cleanup(&self.device); for buffer in &mut self.buffers { self.allocator.free_buffer_allocation(&self.device, buffer); } std::mem::ManuallyDrop::drop(&mut self.allocator); self.device.destroy_render_pass(self.renderpass, None); self.swapchain.cleanup(&self.device); std::mem::ManuallyDrop::drop(&mut self.surface); self.device.destroy_device(None); std::mem::ManuallyDrop::drop(&mut self.debug); self.ash_instance.destroy_instance(None); }; } fn application_info() -> vk::ApplicationInfo<'static> { vk::ApplicationInfo::default() .application_name(APP_NAME) .application_version(0) .engine_name(ENGINE_NAME) .engine_version(0) .api_version(vk::make_api_version(0, 1, 3, 281)) } fn instance_create_info<'a>( debugcreateinfo: &'a mut vk::DebugUtilsMessengerCreateInfoEXT, app_info: &'a vk::ApplicationInfo, layers: &'a [*const c_char], extensions: &'a [*const c_char], ) -> vk::InstanceCreateInfo<'a> { // Creation info vk::InstanceCreateInfo::default() .push_next(debugcreateinfo) .application_info(app_info) .enabled_layer_names(layers) .enabled_extension_names(extensions) } fn init_instance( entry: &ash::Entry, layers: &[*const c_char], extensions: &[*const c_char], ) -> Result { let app_info = Self::application_info(); // enable debug for instance init let mut debugcreateinfo = DebugInstance::debug_utils_messenger_create_info(); let instance_create_info = Self::instance_create_info(&mut debugcreateinfo, &app_info, &layers, &extensions); let instance = unsafe { entry.create_instance(&instance_create_info, None)? }; Ok(instance) } pub fn layer_names() -> Vec { vec![CString::new("VK_LAYER_KHRONOS_validation").unwrap()] } fn select_physical_device( instance: &ash::Instance, ) -> Result<(vk::PhysicalDevice, vk::PhysicalDeviceProperties), EngineError> { let phys_devs: Vec = unsafe { instance.enumerate_physical_devices()? }; let mut primary = None; let mut secondary = None; let mut tertiary = None; let mut fallback = None; for p in phys_devs { let properties = unsafe { instance.get_physical_device_properties(p) }; match properties.device_type { vk::PhysicalDeviceType::DISCRETE_GPU => { // if a dedicated is present, prioritize it primary = Some((p, properties)); break; } vk::PhysicalDeviceType::INTEGRATED_GPU => { // integrated as secondary secondary = Some((p, properties)); } vk::PhysicalDeviceType::VIRTUAL_GPU => { // virtual as tertiary tertiary = Some((p, properties)); } _ => { fallback = Some((p, properties)); } } } let r = if let Some(v) = primary { v } else if let Some(v) = secondary { v } else if let Some(v) = tertiary { v } else { fallback.unwrap() }; Ok(r) } fn device_extensions() -> Vec { vec![ash::khr::swapchain::NAME.into()] } fn create_device( instance: &ash::Instance, physical_device: vk::PhysicalDevice, enabled_layer_names: &[*const i8], queue_infos: &[vk::DeviceQueueCreateInfo], ) -> Result { //TODO: dedicated function let device_extension_names = Self::device_extensions(); let device_extension_name_pointers = as_pointers(&device_extension_names); let device_create_info = vk::DeviceCreateInfo::default() .queue_create_infos(&queue_infos) .enabled_extension_names(&device_extension_name_pointers) .enabled_layer_names(enabled_layer_names); //deprecated, only for compatibility reason let logical_device = unsafe { instance.create_device(physical_device, &device_create_info, None)? }; Ok(logical_device) } fn init_renderpass( device: &ash::Device, surface_format: vk::Format, ) -> Result { let attachments = [vk::AttachmentDescription::default() .format(surface_format) .load_op(vk::AttachmentLoadOp::CLEAR) .store_op(vk::AttachmentStoreOp::STORE) .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) .initial_layout(vk::ImageLayout::UNDEFINED) .final_layout(vk::ImageLayout::PRESENT_SRC_KHR) .samples(vk::SampleCountFlags::TYPE_1)]; let color_attachment_references = [vk::AttachmentReference { attachment: 0, layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, }]; let subpasses = [vk::SubpassDescription::default() .color_attachments(&color_attachment_references) .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)]; let subpass_dependencies = [vk::SubpassDependency::default() .src_subpass(vk::SUBPASS_EXTERNAL) .src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) .dst_subpass(0) .dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) .dst_access_mask( vk::AccessFlags::COLOR_ATTACHMENT_READ | vk::AccessFlags::COLOR_ATTACHMENT_WRITE, )]; let renderpass_info = vk::RenderPassCreateInfo::default() .attachments(&attachments) .subpasses(&subpasses) .dependencies(&subpass_dependencies); let renderpass = unsafe { device.create_render_pass(&renderpass_info, None)? }; Ok(renderpass) } fn fill_commandbuffer( device: &ash::Device, commandbuffer: vk::CommandBuffer, renderpass: vk::RenderPass, swapchain: &Swapchain, framebuffer_index: usize, pipeline: &Pipeline, // buffers: &[vk::Buffer], buffer1: vk::Buffer, buffer2: vk::Buffer, ) -> Result<(), vk::Result> { let commandbuffer_begininfo = vk::CommandBufferBeginInfo::default(); unsafe { device.begin_command_buffer(commandbuffer, &commandbuffer_begininfo)?; } let clearvalues = [vk::ClearValue { color: vk::ClearColorValue { float32: [0.0, 0.0, 0.08, 1.0], }, }]; let renderpass_begininfo = vk::RenderPassBeginInfo::default() .render_pass(renderpass) .framebuffer(swapchain.framebuffers[framebuffer_index]) .render_area(vk::Rect2D { offset: vk::Offset2D { x: 0, y: 0 }, extent: swapchain.extent, }) .clear_values(&clearvalues); unsafe { device.cmd_begin_render_pass( commandbuffer, &renderpass_begininfo, vk::SubpassContents::INLINE, ); device.cmd_bind_pipeline( commandbuffer, vk::PipelineBindPoint::GRAPHICS, pipeline.pipeline, ); device.cmd_bind_vertex_buffers(commandbuffer, 0, &[buffer1], &[0]); device.cmd_bind_vertex_buffers(commandbuffer, 1, &[buffer2], &[0]); device.cmd_draw(commandbuffer, 1, 1, 0, 0); device.cmd_end_render_pass(commandbuffer); device.end_command_buffer(commandbuffer)?; } Ok(()) } } impl Drop for EngineInstance { fn drop(&mut self) { self.destroy() } } fn as_pointers(value: &[CString]) -> Vec<*const i8> { value.iter().map(|layer_name| layer_name.as_ptr()).collect() }