use std::default::Default;
use std::ffi::CString;
use ash::vk;
use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc};
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
mod imgui_renderer;
use imgui_renderer::{handle_imgui_event, ImGuiRenderer};
mod helper;
use helper::record_and_submit_command_buffer;
fn main() -> ash::prelude::VkResult<()> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init();
let entry = unsafe { ash::Entry::load() }.unwrap();
let event_loop = winit::event_loop::EventLoop::new();
let window_width = 1920;
let window_height = 1080;
let window = winit::window::WindowBuilder::new()
.with_title("gpu-allocator Vulkan visualization")
.with_inner_size(winit::dpi::PhysicalSize::new(
window_width as f64,
window_height as f64,
))
.with_resizable(false)
.build(&event_loop)
.unwrap();
let instance = {
let app_name = CString::new("gpu-allocator examples vulkan-visualization").unwrap();
let appinfo = vk::ApplicationInfo::builder()
.application_name(&app_name)
.application_version(0)
.engine_name(&app_name)
.engine_version(0)
.api_version(vk::make_api_version(0, 1, 0, 0));
let layer_names: &[CString] = &[CString::new("VK_LAYER_KHRONOS_validation").unwrap()];
let layers_names_raw: Vec<*const i8> = layer_names
.iter()
.map(|raw_name| raw_name.as_ptr())
.collect();
let surface_extensions =
ash_window::enumerate_required_extensions(event_loop.raw_display_handle()).unwrap();
let create_info = vk::InstanceCreateInfo::builder()
.application_info(&appinfo)
.enabled_layer_names(&layers_names_raw)
.enabled_extension_names(surface_extensions);
unsafe {
entry
.create_instance(&create_info, None)
.expect("Instance creation error")
}
};
let surface = unsafe {
ash_window::create_surface(
&entry,
&instance,
window.raw_display_handle(),
window.raw_window_handle(),
None,
)
}
.unwrap();
let surface_loader = ash::extensions::khr::Surface::new(&entry, &instance);
let (pdevice, queue_family_index) = {
let pdevices = unsafe {
instance
.enumerate_physical_devices()
.expect("Physical device error")
};
pdevices
.iter()
.find_map(|pdevice| {
unsafe { instance.get_physical_device_queue_family_properties(*pdevice) }
.iter()
.enumerate()
.find_map(|(index, &info)| {
let supports_graphics = info.queue_flags.contains(vk::QueueFlags::GRAPHICS);
let supports_surface = unsafe {
surface_loader.get_physical_device_surface_support(
*pdevice,
index as u32,
surface,
)
}
.unwrap();
if supports_graphics && supports_surface {
Some((*pdevice, index))
} else {
None
}
})
})
.expect("Couldn't find suitable device.")
};
let device = {
let device_extension_names_raw = [ash::extensions::khr::Swapchain::name().as_ptr()];
let features = vk::PhysicalDeviceFeatures {
shader_clip_distance: 1,
..Default::default()
};
let priorities = [1.0];
let queue_info = vk::DeviceQueueCreateInfo::builder()
.queue_family_index(queue_family_index as u32)
.queue_priorities(&priorities);
let create_info = vk::DeviceCreateInfo::builder()
.queue_create_infos(std::slice::from_ref(&queue_info))
.enabled_extension_names(&device_extension_names_raw)
.enabled_features(&features);
unsafe { instance.create_device(pdevice, &create_info, None) }.unwrap()
};
let present_queue = unsafe { device.get_device_queue(queue_family_index as u32, 0) };
let surface_format =
unsafe { surface_loader.get_physical_device_surface_formats(pdevice, surface) }.unwrap()[0];
let surface_capabilities =
unsafe { surface_loader.get_physical_device_surface_capabilities(pdevice, surface) }
.unwrap();
let mut desired_image_count = surface_capabilities.min_image_count + 1;
if surface_capabilities.max_image_count > 0
&& desired_image_count > surface_capabilities.max_image_count
{
desired_image_count = surface_capabilities.max_image_count;
}
let surface_resolution = match surface_capabilities.current_extent.width {
u32::MAX => vk::Extent2D {
width: window_width,
height: window_height,
},
_ => surface_capabilities.current_extent,
};
let pre_transform = if surface_capabilities
.supported_transforms
.contains(vk::SurfaceTransformFlagsKHR::IDENTITY)
{
vk::SurfaceTransformFlagsKHR::IDENTITY
} else {
surface_capabilities.current_transform
};
let present_modes =
unsafe { surface_loader.get_physical_device_surface_present_modes(pdevice, surface) }
.unwrap();
let present_mode = present_modes
.iter()
.cloned()
.find(|&mode| mode == vk::PresentModeKHR::MAILBOX)
.unwrap_or(vk::PresentModeKHR::FIFO);
let swapchain_loader = ash::extensions::khr::Swapchain::new(&instance, &device);
let swapchain_create_info = vk::SwapchainCreateInfoKHR::builder()
.surface(surface)
.min_image_count(desired_image_count)
.image_color_space(surface_format.color_space)
.image_format(surface_format.format)
.image_extent(surface_resolution)
.image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
.image_sharing_mode(vk::SharingMode::EXCLUSIVE)
.pre_transform(pre_transform)
.composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
.present_mode(present_mode)
.clipped(true)
.image_array_layers(1);
let swapchain =
unsafe { swapchain_loader.create_swapchain(&swapchain_create_info, None) }.unwrap();
let pool_create_info = vk::CommandPoolCreateInfo::builder()
.flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER)
.queue_family_index(queue_family_index as u32);
let command_pool = unsafe { device.create_command_pool(&pool_create_info, None) }.unwrap();
let command_buffer_allocate_info = vk::CommandBufferAllocateInfo::builder()
.command_buffer_count(2)
.command_pool(command_pool)
.level(vk::CommandBufferLevel::PRIMARY);
let command_buffers =
unsafe { device.allocate_command_buffers(&command_buffer_allocate_info) }.unwrap();
let setup_command_buffer = command_buffers[0];
let draw_command_buffer = command_buffers[1];
let present_images = unsafe { swapchain_loader.get_swapchain_images(swapchain) }.unwrap();
let mut present_image_views = present_images
.iter()
.map(|&image| {
let create_view_info = vk::ImageViewCreateInfo::builder()
.view_type(vk::ImageViewType::TYPE_2D)
.format(surface_format.format)
.components(vk::ComponentMapping {
r: vk::ComponentSwizzle::R,
g: vk::ComponentSwizzle::G,
b: vk::ComponentSwizzle::B,
a: vk::ComponentSwizzle::A,
})
.subresource_range(vk::ImageSubresourceRange {
aspect_mask: vk::ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: 1,
base_array_layer: 0,
layer_count: 1,
})
.image(image);
unsafe { device.create_image_view(&create_view_info, None) }.unwrap()
})
.collect::<Vec<_>>();
let mut allocator = Some(
Allocator::new(&AllocatorCreateDesc {
instance: instance.clone(),
device: device.clone(),
physical_device: pdevice,
debug_settings: Default::default(),
buffer_device_address: false,
})
.unwrap(),
);
let fence_create_info = vk::FenceCreateInfo::builder().flags(vk::FenceCreateFlags::SIGNALED);
let draw_commands_reuse_fence =
unsafe { device.create_fence(&fence_create_info, None) }.unwrap();
let setup_commands_reuse_fence =
unsafe { device.create_fence(&fence_create_info, None) }.unwrap();
let semaphore_create_info = vk::SemaphoreCreateInfo::default();
let present_complete_semaphore =
unsafe { device.create_semaphore(&semaphore_create_info, None) }.unwrap();
let rendering_complete_semaphore =
unsafe { device.create_semaphore(&semaphore_create_info, None) }.unwrap();
let mut imgui = imgui::Context::create();
imgui.io_mut().display_size = [window_width as f32, window_height as f32];
let descriptor_pool = {
let pool_sizes = [
vk::DescriptorPoolSize {
ty: vk::DescriptorType::UNIFORM_BUFFER,
descriptor_count: 1,
},
vk::DescriptorPoolSize {
ty: vk::DescriptorType::SAMPLED_IMAGE,
descriptor_count: 1,
},
vk::DescriptorPoolSize {
ty: vk::DescriptorType::SAMPLER,
descriptor_count: 1,
},
];
let create_info = vk::DescriptorPoolCreateInfo::builder()
.max_sets(1)
.pool_sizes(&pool_sizes);
unsafe { device.create_descriptor_pool(&create_info, None) }?
};
let mut imgui_renderer = Some(ImGuiRenderer::new(
&mut imgui,
&device,
descriptor_pool,
surface_format.format,
allocator.as_mut().unwrap(),
setup_command_buffer,
setup_commands_reuse_fence,
present_queue,
)?);
let mut framebuffers = present_image_views
.iter()
.map(|&view| {
let create_info = vk::FramebufferCreateInfo::builder()
.render_pass(imgui_renderer.as_ref().unwrap().render_pass)
.attachments(std::slice::from_ref(&view))
.width(window_width)
.height(window_height)
.layers(1);
unsafe { device.create_framebuffer(&create_info, None) }.unwrap()
})
.collect::<Vec<_>>();
let mut visualizer = Some(gpu_allocator::vulkan::AllocatorVisualizer::new());
event_loop.run(move |event, _, control_flow| {
*control_flow = winit::event_loop::ControlFlow::Wait;
handle_imgui_event(imgui.io_mut(), &window, &event);
let mut ready_for_rendering = false;
match event {
winit::event::Event::WindowEvent { event, .. } => match event {
winit::event::WindowEvent::CloseRequested
| winit::event::WindowEvent::KeyboardInput {
input:
winit::event::KeyboardInput {
virtual_keycode: Some(winit::event::VirtualKeyCode::Escape),
..
},
..
} => {
*control_flow = winit::event_loop::ControlFlow::Exit;
}
_ => {}
},
winit::event::Event::MainEventsCleared => ready_for_rendering = true,
_ => {}
}
if ready_for_rendering {
let (present_index, _) = unsafe {
swapchain_loader.acquire_next_image(
swapchain,
u64::MAX,
present_complete_semaphore,
vk::Fence::null(),
)
}
.unwrap();
let ui = imgui.frame();
visualizer
.as_mut()
.unwrap()
.render(allocator.as_ref().unwrap(), ui, None);
let imgui_draw_data = imgui.render();
record_and_submit_command_buffer(
&device,
draw_command_buffer,
draw_commands_reuse_fence,
present_queue,
&[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT],
&[present_complete_semaphore],
&[rendering_complete_semaphore],
|device, cmd| {
imgui_renderer.as_mut().unwrap().render(
imgui_draw_data,
device,
window_width,
window_height,
framebuffers[present_index as usize],
cmd,
);
let image_barriers = vk::ImageMemoryBarrier::builder()
.src_access_mask(
vk::AccessFlags::COLOR_ATTACHMENT_READ
| vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
)
.old_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
.new_layout(vk::ImageLayout::PRESENT_SRC_KHR)
.image(present_images[present_index as usize])
.subresource_range(vk::ImageSubresourceRange {
aspect_mask: vk::ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: vk::REMAINING_MIP_LEVELS,
base_array_layer: 0,
layer_count: vk::REMAINING_ARRAY_LAYERS,
});
unsafe {
device.cmd_pipeline_barrier(
cmd,
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
vk::PipelineStageFlags::BOTTOM_OF_PIPE,
vk::DependencyFlags::empty(),
&[],
&[],
std::slice::from_ref(&image_barriers),
)
};
},
);
let present_create_info = vk::PresentInfoKHR::builder()
.wait_semaphores(std::slice::from_ref(&rendering_complete_semaphore))
.swapchains(std::slice::from_ref(&swapchain))
.image_indices(std::slice::from_ref(&present_index));
unsafe { swapchain_loader.queue_present(present_queue, &present_create_info) }.unwrap();
} else if *control_flow == winit::event_loop::ControlFlow::Exit {
unsafe { device.queue_wait_idle(present_queue) }.unwrap();
visualizer.take();
for fb in framebuffers.drain(..) {
unsafe { device.destroy_framebuffer(fb, None) };
}
let mut allocator = allocator.take().unwrap();
imgui_renderer
.take()
.unwrap()
.destroy(&device, &mut allocator);
unsafe { device.destroy_descriptor_pool(descriptor_pool, None) };
unsafe { device.destroy_semaphore(rendering_complete_semaphore, None) };
unsafe { device.destroy_semaphore(present_complete_semaphore, None) };
unsafe { device.destroy_fence(setup_commands_reuse_fence, None) };
unsafe { device.destroy_fence(draw_commands_reuse_fence, None) };
drop(allocator);
for view in present_image_views.drain(..) {
unsafe { device.destroy_image_view(view, None) };
}
unsafe { device.free_command_buffers(command_pool, &command_buffers) };
unsafe { device.destroy_command_pool(command_pool, None) };
unsafe { swapchain_loader.destroy_swapchain(swapchain, None) };
unsafe { device.destroy_device(None) };
unsafe {
surface_loader.destroy_surface(surface, None);
}
unsafe { instance.destroy_instance(None) };
}
});
}