vulkan-intro-rs/src/instance/mod.rs
2024-07-31 10:14:20 +02:00

443 lines
15 KiB
Rust

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<DebugInstance>,
pub surface: std::mem::ManuallyDrop<Surface>,
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<vk::CommandBuffer>,
pub allocator: std::mem::ManuallyDrop<BufferAllocator>,
pub buffers: Vec<Buffer>,
pub framecount: FrameCounter,
}
impl EngineInstance {
pub fn init(window: &Window) -> Result<Self, EngineError> {
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<ash::Instance, EngineError> {
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<CString> {
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<vk::PhysicalDevice> = 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<CString> {
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<ash::Device, EngineError> {
//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<vk::RenderPass, vk::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()
}