use erupt::{vk, DeviceLoader, EntryLoader, InstanceLoader};
use erupt_bootstrap::{
DeviceBuilder, InstanceBuilder, QueueFamilyCriteria, Swapchain, SwapchainOptions,
};
use std::time::Instant;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder},
};
fn main() {
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
let mut app = App::new(&window);
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
Event::WindowEvent {
event: WindowEvent::Resized(size),
..
} => {
app.resize(vk::Extent2D {
width: size.width,
height: size.height,
});
}
Event::MainEventsCleared => {
app.draw();
}
_ => (),
}
});
}
pub struct App {
device: DeviceLoader,
instance: InstanceLoader,
_entry: EntryLoader,
surface: vk::SurfaceKHR,
epoch: Instant,
swapchain: Swapchain,
queue: vk::Queue,
command_pool: vk::CommandPool,
frames: Vec<Frame>,
}
impl App {
pub fn new(window: &Window) -> Self {
unsafe {
let entry = EntryLoader::new().unwrap();
let instance_builder = InstanceBuilder::new()
.require_surface_extensions(&window)
.unwrap();
let (instance, _debug_messenger, instance_metadata) =
instance_builder.build(&entry).unwrap();
let surface = erupt::utils::surface::create_surface(&instance, &window, None).unwrap();
let graphics_present = QueueFamilyCriteria::graphics_present();
let device_builder = DeviceBuilder::new()
.require_extension(vk::KHR_SWAPCHAIN_EXTENSION_NAME)
.queue_family(graphics_present)
.for_surface(surface);
let (device, device_metadata) =
device_builder.build(&instance, &instance_metadata).unwrap();
let (graphics_present, graphics_present_idx) = device_metadata
.device_queue(&instance, &device, graphics_present, 0)
.unwrap()
.unwrap();
let size = window.inner_size();
let mut options = SwapchainOptions::default();
options.usage(vk::ImageUsageFlags::TRANSFER_DST); let swapchain = Swapchain::new(
options,
surface,
device_metadata.physical_device(),
&device,
vk::Extent2D {
width: size.width,
height: size.height,
},
);
let command_pool = device
.create_command_pool(
&vk::CommandPoolCreateInfoBuilder::new()
.flags(
vk::CommandPoolCreateFlags::TRANSIENT
| vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER,
)
.queue_family_index(graphics_present_idx),
None,
)
.unwrap();
let cmds = device
.allocate_command_buffers(
&vk::CommandBufferAllocateInfoBuilder::new()
.command_pool(command_pool)
.level(vk::CommandBufferLevel::PRIMARY)
.command_buffer_count(swapchain.frames_in_flight() as u32),
)
.unwrap();
let frames = cmds
.into_iter()
.map(|cmd| Frame {
cmd,
complete: device
.create_semaphore(&vk::SemaphoreCreateInfo::default(), None)
.unwrap(),
})
.collect();
Self {
_entry: entry,
instance,
surface,
epoch: Instant::now(),
device,
swapchain,
queue: graphics_present,
command_pool,
frames,
}
}
}
fn resize(&mut self, size: vk::Extent2D) {
self.swapchain.update(size);
}
fn draw(&mut self) {
unsafe {
let acq = self
.swapchain
.acquire(&self.instance, &self.device, !0)
.unwrap();
let cmd = self.frames[acq.frame_index].cmd;
let swapchain_image = self.swapchain.images()[acq.image_index];
self.device
.begin_command_buffer(
cmd,
&vk::CommandBufferBeginInfoBuilder::new()
.flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
)
.unwrap();
self.device.cmd_pipeline_barrier(
cmd,
vk::PipelineStageFlags::TRANSFER,
vk::PipelineStageFlags::TRANSFER,
vk::DependencyFlags::default(),
&[],
&[],
&[vk::ImageMemoryBarrierBuilder::new()
.dst_access_mask(vk::AccessFlags::TRANSFER_WRITE)
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.old_layout(vk::ImageLayout::UNDEFINED)
.new_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
.image(swapchain_image)
.subresource_range(vk::ImageSubresourceRange {
aspect_mask: vk::ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: 1,
base_array_layer: 0,
layer_count: 1,
})],
);
let t = (self.epoch.elapsed().as_secs_f32().sin() + 1.0) * 0.5;
self.device.cmd_clear_color_image(
cmd,
swapchain_image,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
&vk::ClearColorValue {
float32: [0.0, t, 0.0, 1.0],
},
&[vk::ImageSubresourceRangeBuilder::new()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.base_mip_level(0)
.level_count(1)
.base_array_layer(0)
.layer_count(1)],
);
self.device.cmd_pipeline_barrier(
cmd,
vk::PipelineStageFlags::TRANSFER,
vk::PipelineStageFlags::BOTTOM_OF_PIPE,
vk::DependencyFlags::default(),
&[],
&[],
&[vk::ImageMemoryBarrierBuilder::new()
.src_access_mask(vk::AccessFlags::TRANSFER_WRITE)
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
.new_layout(vk::ImageLayout::PRESENT_SRC_KHR)
.image(swapchain_image)
.subresource_range(vk::ImageSubresourceRange {
aspect_mask: vk::ImageAspectFlags::COLOR,
base_mip_level: 0,
level_count: 1,
base_array_layer: 0,
layer_count: 1,
})],
);
self.device.end_command_buffer(cmd).unwrap();
self.device
.queue_submit(
self.queue,
&[vk::SubmitInfoBuilder::new()
.wait_semaphores(&[acq.ready])
.wait_dst_stage_mask(&[vk::PipelineStageFlags::TRANSFER])
.signal_semaphores(&[self.frames[acq.frame_index].complete])
.command_buffers(&[cmd])],
acq.complete,
)
.unwrap();
self.swapchain
.queue_present(
&self.device,
self.queue,
self.frames[acq.frame_index].complete,
acq.image_index,
)
.unwrap();
}
}
}
impl Drop for App {
fn drop(&mut self) {
unsafe {
let _ = self.device.device_wait_idle();
for frame in &self.frames {
self.device.destroy_semaphore(frame.complete, None);
}
self.device.destroy_command_pool(self.command_pool, None);
self.swapchain.destroy(&self.device);
self.instance.destroy_surface_khr(self.surface, None);
self.device.destroy_device(None);
self.instance.destroy_instance(None);
}
}
}
struct Frame {
cmd: vk::CommandBuffer,
complete: vk::Semaphore,
}