499 lines
17 KiB
Rust
499 lines
17 KiB
Rust
#[allow(unused_imports)]
|
|
use log::{debug, error, info, trace, warn};
|
|
|
|
use std::{
|
|
mem::ManuallyDrop,
|
|
iter,
|
|
};
|
|
|
|
use core::ptr::read;
|
|
|
|
use gfx_hal::{
|
|
Instance,
|
|
device::Device,
|
|
queue::QueueGroup,
|
|
Backend,
|
|
pool::CommandPool,
|
|
pso::Rect,
|
|
};
|
|
|
|
use gfx_backend_vulkan as vk_back;
|
|
|
|
use winit::window::Window;
|
|
|
|
pub mod render;
|
|
|
|
#[derive(Debug)]
|
|
pub struct HalState {
|
|
//items need to communicate with the GPU
|
|
instance: ManuallyDrop<<vk_back::Backend as Backend>::Instance>,
|
|
surface: ManuallyDrop<<vk_back::Backend as Backend>::Surface>,
|
|
adapter: ManuallyDrop<gfx_hal::adapter::Adapter<vk_back::Backend>>,
|
|
device: vk_back::Device,
|
|
queue_group: ManuallyDrop<QueueGroup<vk_back::Backend>>,
|
|
render_pass: ManuallyDrop<<vk_back::Backend as Backend>::RenderPass>,
|
|
swapchain: ManuallyDrop<<vk_back::Backend as Backend>::Swapchain>,
|
|
extent: gfx_hal::window::Extent2D,
|
|
format: gfx_hal::format::Format,
|
|
render_area: Rect,
|
|
|
|
//items needed to render the images
|
|
sems_image_available: Vec<<vk_back::Backend as Backend>::Semaphore>,
|
|
sems_render_finished: Vec<<vk_back::Backend as Backend>::Semaphore>,
|
|
fences: Vec<<vk_back::Backend as Backend>::Fence>,
|
|
image_views: Vec<<vk_back::Backend as Backend>::ImageView>,
|
|
framebuffers: Vec<<vk_back::Backend as Backend>::Framebuffer>,
|
|
|
|
command_pool: ManuallyDrop<<vk_back::Backend as Backend>::CommandPool>,
|
|
command_buffers: Vec<<vk_back::Backend as Backend>::CommandBuffer>,
|
|
|
|
//items needed to keep track of the images
|
|
image_count: usize,
|
|
current_image: usize,
|
|
}
|
|
|
|
impl core::ops::Drop for HalState {
|
|
|
|
//----------------------------------------------------------------------------------------------
|
|
fn drop(&mut self) {
|
|
|
|
let _ = self.device.wait_idle();
|
|
|
|
//destroy all underlying ressources
|
|
debug!("Destroying HAL ressources");
|
|
unsafe {
|
|
self.command_pool.free(self.command_buffers.drain(..));
|
|
self.device.destroy_command_pool(
|
|
ManuallyDrop::into_inner(read(&mut self.command_pool)));
|
|
for buffer in self.framebuffers.drain(..) {
|
|
self.device.destroy_framebuffer(buffer);
|
|
}
|
|
for view in self.image_views.drain(..) {
|
|
self.device.destroy_image_view(view);
|
|
}
|
|
for fence in self.fences.drain(..) {
|
|
self.device.destroy_fence(fence);
|
|
}
|
|
for sem in self.sems_image_available.drain(..) {
|
|
self.device.destroy_semaphore(sem);
|
|
}
|
|
for sem in self.sems_render_finished.drain(..) {
|
|
self.device.destroy_semaphore(sem);
|
|
}
|
|
self.device.destroy_swapchain(ManuallyDrop::into_inner(read(&mut self.swapchain)));
|
|
self.device.destroy_render_pass(ManuallyDrop::into_inner(read(&mut self.render_pass)));
|
|
ManuallyDrop::drop(&mut self.queue_group);
|
|
ManuallyDrop::drop(&mut self.adapter);
|
|
self.instance.destroy_surface(ManuallyDrop::into_inner(read(&mut self.surface)));
|
|
ManuallyDrop::drop(&mut self.instance);
|
|
}
|
|
info!("HAL ressources destroyed");
|
|
}
|
|
}
|
|
|
|
impl HalState {
|
|
|
|
//----------------------------------------------------------------------------------------------
|
|
pub fn recreate_swapchain(&mut self) -> Result<(), &'static str> {
|
|
use gfx_hal::window::{
|
|
SwapchainConfig,
|
|
Surface,
|
|
};
|
|
|
|
debug!("Recreating swapchain");
|
|
|
|
//destroying previous swapchain
|
|
unsafe {
|
|
for buffer in self.framebuffers.drain(..) {
|
|
self.device.destroy_framebuffer(buffer);
|
|
}
|
|
for view in self.image_views.drain(..) {
|
|
self.device.destroy_image_view(view);
|
|
}
|
|
|
|
self.device.destroy_swapchain(ManuallyDrop::into_inner(read(&mut self.swapchain)));
|
|
}
|
|
let capabilities = self.surface.capabilities(&self.adapter.physical_device);
|
|
info!("{:#?}", capabilities);
|
|
|
|
//creating new swapchain
|
|
let swapchain_config = SwapchainConfig::from_caps(&capabilities, self.format, self.extent);
|
|
info!("{:?}", swapchain_config);
|
|
|
|
let (swapchain, backbuffer) = unsafe {
|
|
self.device
|
|
.create_swapchain(&mut self.surface, swapchain_config, None)
|
|
.map_err(|_| "Failed to create swapchain and backbuffer")?
|
|
};
|
|
|
|
let image_views : Vec<_> = {
|
|
use gfx_hal::{
|
|
image::{ViewKind, SubresourceRange},
|
|
format::{Swizzle, Aspects},
|
|
};
|
|
|
|
backbuffer
|
|
.iter()
|
|
.map(|img| unsafe {
|
|
self.device
|
|
.create_image_view(
|
|
&img,
|
|
ViewKind::D2,
|
|
self.format,
|
|
Swizzle::NO,
|
|
SubresourceRange {
|
|
aspects : Aspects::COLOR,
|
|
levels : 0..1,
|
|
layers : 0..1,
|
|
},
|
|
)
|
|
.map_err(|_| "Could not create ImageViews")
|
|
})
|
|
.collect::<Result<Vec<_>, &str>>()?
|
|
};
|
|
|
|
let framebuffers: Vec<_> = {
|
|
|
|
image_views
|
|
.iter()
|
|
.map(|image_view|
|
|
unsafe {
|
|
self.device
|
|
.create_framebuffer(&self.render_pass,
|
|
iter::once(image_view),
|
|
self.extent.to_extent())
|
|
.map_err(|_| "Could not create FrameBuffer")
|
|
},
|
|
)
|
|
.collect::<Result<Vec<_>, &str>>()?
|
|
};
|
|
|
|
self.swapchain = ManuallyDrop::new(swapchain);
|
|
self.image_views = image_views;
|
|
self.framebuffers = framebuffers;
|
|
Ok(())
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------
|
|
pub fn new(window: &Window) -> Result<HalState, &'static str> {
|
|
use gfx_hal::adapter::Gpu;
|
|
|
|
// create top level
|
|
let instance = vk_back::Instance::create("IV", 1)
|
|
.map_err(|_| "Could not create instance")?;
|
|
|
|
let mut surface = unsafe {
|
|
instance
|
|
.create_surface(window)
|
|
.map_err(|_| "Could not create Surface")?
|
|
};
|
|
|
|
let (adapter, device, queue_group) = {
|
|
use gfx_hal::{
|
|
window::Surface,
|
|
queue::family::QueueFamily,
|
|
};
|
|
|
|
// find a adapter with a suitable queue family
|
|
// TODO add weight for adapters
|
|
let adapter =
|
|
Instance::enumerate_adapters(&instance)
|
|
.into_iter();
|
|
// .find(|a| {
|
|
// a.queue_families
|
|
// .iter()
|
|
// .any(|qf|
|
|
// QueueFamily::queue_type(qf).supports_graphics()
|
|
// && Surface::supports_queue_family(&surface, &qf)
|
|
// )})
|
|
// .ok_or("Could not find a graphical adapter")?;
|
|
|
|
debug!("Adapters : {:#?}", adapter);
|
|
|
|
let adapter =
|
|
Instance::enumerate_adapters(&instance)
|
|
.into_iter()
|
|
.find(|a| {
|
|
a.queue_families
|
|
.iter()
|
|
.any(|qf|
|
|
QueueFamily::queue_type(qf).supports_graphics()
|
|
&& Surface::supports_queue_family(&surface, &qf)
|
|
)})
|
|
.ok_or("Could not find a graphical adapter")?;
|
|
|
|
info!("Selected adapter : {}", adapter.info.name);
|
|
|
|
// get the suitable queue family index
|
|
let queue_family = adapter
|
|
.queue_families
|
|
.iter()
|
|
.find(|qf|
|
|
QueueFamily::queue_type(*qf).supports_graphics()
|
|
&& Surface::supports_queue_family(&surface, &qf))
|
|
.ok_or("Could not find suitable queue_family")?;
|
|
|
|
// get the related physical device and queue family list
|
|
let Gpu {device, queue_groups} = unsafe {
|
|
use gfx_hal::{
|
|
adapter::PhysicalDevice,
|
|
Features,
|
|
};
|
|
|
|
adapter
|
|
.physical_device
|
|
.open(&[(&queue_family, &[1.0; 1])], Features::empty())
|
|
.map_err(|_| "Could not open physical device")?
|
|
};
|
|
|
|
// retrieve the selected queue family
|
|
let queue_group = queue_groups
|
|
.into_iter()
|
|
.find(|qg| qg.family == queue_family.id())
|
|
.ok_or("Could not take ownership of the queue")?;
|
|
|
|
// check our harvest
|
|
if queue_group.queues.len() <= 0 {
|
|
return Err("The QueueGroup does not have any CommandQueues available");
|
|
};
|
|
|
|
(adapter, device, queue_group)
|
|
};
|
|
|
|
let (swapchain, extent, backbuffer, format, image_count) = {
|
|
use gfx_hal::window::{
|
|
SwapchainConfig,
|
|
Surface,
|
|
};
|
|
|
|
let capabilities = surface.capabilities(&adapter.physical_device);
|
|
debug!("{:#?}", capabilities);
|
|
|
|
// select optimal presentation mode (vsync, triple buffuring, ...)
|
|
let present_mode = {
|
|
use gfx_hal::window::PresentMode;
|
|
|
|
[PresentMode::MAILBOX, PresentMode::FIFO,
|
|
PresentMode::RELAXED, PresentMode::IMMEDIATE]
|
|
.iter()
|
|
.cloned()
|
|
.find(|pm| capabilities.present_modes.contains(*pm))
|
|
.ok_or("No PresentMode found")?
|
|
};
|
|
|
|
// select optimal alpha composition
|
|
// let composite_alpha_mode = {
|
|
// use gfx_hal::window::CompositeAlphaMode;
|
|
//
|
|
// [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT,
|
|
// CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::POSTMULTIPLIED]
|
|
// .iter()
|
|
// .cloned()
|
|
// .find(|cam| capabilities.composite_alpha_modes.contains(*cam))
|
|
// .ok_or("No CompositeAlphaMode found")?
|
|
// };
|
|
|
|
// select optimal format (sRGB)
|
|
let format = {
|
|
use gfx_hal::format::{Format, ChannelType};
|
|
|
|
match surface.supported_formats(&adapter.physical_device) {
|
|
None => Format::Rgba8Srgb,
|
|
Some(formats) => formats
|
|
.iter()
|
|
.find(|f| f.base_format().1 == ChannelType::Srgb)
|
|
.cloned()
|
|
.ok_or("Could no find suitabe format")?
|
|
}};
|
|
|
|
let extent = capabilities.extents.end().clone();
|
|
|
|
// verify swapchain size
|
|
let image_count = {
|
|
use gfx_hal::window::PresentMode;
|
|
|
|
capabilities.image_count.end()
|
|
.min(capabilities.image_count.start()
|
|
.max(match present_mode {
|
|
PresentMode::MAILBOX => &3,
|
|
PresentMode::FIFO => &2,
|
|
_ => &1,
|
|
})).clone()
|
|
};
|
|
debug!("image count : {}", image_count);
|
|
|
|
// // a surface support at least one layer
|
|
// let image_layers = 1;
|
|
//
|
|
// // verify surface compatibility
|
|
// let image_usage = {
|
|
// use gfx_hal::image::Usage;
|
|
//
|
|
// if capabilities.usage.contains(Usage::COLOR_ATTACHMENT) {
|
|
// Ok(Usage::COLOR_ATTACHMENT)
|
|
// } else {
|
|
// Err("This surface does not support color")
|
|
// }
|
|
// }?;
|
|
|
|
let swapchain_config = SwapchainConfig::from_caps(&capabilities, format, extent);
|
|
debug!("{:?}", swapchain_config);
|
|
|
|
let (swapchain, backbuffer) = unsafe {
|
|
device
|
|
.create_swapchain(&mut surface, swapchain_config, None)
|
|
.map_err(|_| "Failed to create swapchain and backbuffer")?
|
|
};
|
|
|
|
(swapchain, extent, backbuffer, format, image_count as usize)
|
|
};
|
|
|
|
// creating semaphores and fences
|
|
let (sems_image_available, sems_render_finished, fences) = {
|
|
|
|
let mut sems_image_available : Vec<<vk_back::Backend as Backend>::Semaphore> = vec![];
|
|
let mut sems_render_finished : Vec<<vk_back::Backend as Backend>::Semaphore> = vec![];
|
|
let mut fences :Vec<<vk_back::Backend as Backend>::Fence> = vec![];
|
|
|
|
for _ in 0..image_count {
|
|
sems_image_available
|
|
.push(device
|
|
.create_semaphore()
|
|
.map_err(|_| "Could not create sempahore")?
|
|
);
|
|
sems_render_finished
|
|
.push(device
|
|
.create_semaphore()
|
|
.map_err(|_| "Could not create sempahore")?
|
|
);
|
|
fences
|
|
.push(device
|
|
.create_fence(true)
|
|
.map_err(|_| "Could not create fence")?
|
|
);
|
|
}
|
|
|
|
(sems_image_available, sems_render_finished, fences)
|
|
};
|
|
|
|
// creating RenderPass
|
|
let render_pass = {
|
|
use gfx_hal::{
|
|
pass::{Attachment, AttachmentOps, AttachmentLoadOp, AttachmentStoreOp, SubpassDesc},
|
|
image::Layout,
|
|
};
|
|
|
|
let color_attachment = Attachment {
|
|
format : Some(format),
|
|
samples : 1,
|
|
ops : AttachmentOps {
|
|
load : AttachmentLoadOp::Clear,
|
|
store : AttachmentStoreOp::Store,
|
|
},
|
|
stencil_ops : AttachmentOps::DONT_CARE,
|
|
layouts : (Layout::Undefined..Layout::Present),
|
|
};
|
|
|
|
let subpass = SubpassDesc {
|
|
colors : &[(0, Layout::ColorAttachmentOptimal)],
|
|
depth_stencil : None,
|
|
inputs : &[],
|
|
resolves : &[],
|
|
preserves : &[],
|
|
};
|
|
|
|
unsafe {
|
|
device.create_render_pass(&[color_attachment], &[subpass], &[])
|
|
.map_err(|_| "Could not create render pass")?
|
|
}
|
|
};
|
|
|
|
// add ImageView "headers" to images in BackBuffer
|
|
let image_views : Vec<_> = {
|
|
use gfx_hal::{
|
|
image::{ViewKind, SubresourceRange},
|
|
format::{Swizzle, Aspects},
|
|
};
|
|
|
|
backbuffer
|
|
.iter()
|
|
.map(|img| unsafe {
|
|
device
|
|
.create_image_view(
|
|
&img,
|
|
ViewKind::D2,
|
|
format,
|
|
Swizzle::NO,
|
|
SubresourceRange {
|
|
aspects : Aspects::COLOR,
|
|
levels : 0..1,
|
|
layers : 0..1,
|
|
},
|
|
)
|
|
.map_err(|_| "Could not create ImageViews")
|
|
})
|
|
.collect::<Result<Vec<_>, &str>>()?
|
|
};
|
|
|
|
let framebuffers: Vec<_> = {
|
|
|
|
image_views
|
|
.iter()
|
|
.map(|image_view|
|
|
unsafe {
|
|
device
|
|
.create_framebuffer(&render_pass, vec![image_view], extent.to_extent())
|
|
.map_err(|_| "Could not create FrameBuffer")
|
|
},
|
|
)
|
|
.collect::<Result<Vec<_>, &str>>()?
|
|
};
|
|
|
|
let mut command_pool = unsafe {
|
|
use gfx_hal::pool::CommandPoolCreateFlags;
|
|
|
|
device
|
|
.create_command_pool(queue_group.family, CommandPoolCreateFlags::RESET_INDIVIDUAL)
|
|
.map_err(|_| "Could not create CommandPool")?
|
|
};
|
|
|
|
let command_buffers: Vec<_> = unsafe {
|
|
use gfx_hal::command::Level;
|
|
|
|
framebuffers
|
|
.iter()
|
|
.map(|_| command_pool.allocate_one(Level::Primary))
|
|
.collect()
|
|
};
|
|
|
|
info!("HAL successfully initialized");
|
|
|
|
Ok(
|
|
HalState {
|
|
instance: ManuallyDrop::new(instance),
|
|
surface: ManuallyDrop::new(surface),
|
|
adapter: ManuallyDrop::new(adapter),
|
|
device: device,
|
|
queue_group: ManuallyDrop::new(queue_group),
|
|
render_pass: ManuallyDrop::new(render_pass),
|
|
swapchain: ManuallyDrop::new(swapchain),
|
|
extent: extent,
|
|
format: format,
|
|
render_area: Rect{x: 0, y: 0, w: 1280, h: 720},
|
|
sems_image_available,
|
|
sems_render_finished,
|
|
fences,
|
|
image_views,
|
|
framebuffers,
|
|
|
|
command_pool: ManuallyDrop::new(command_pool),
|
|
command_buffers,
|
|
|
|
image_count,
|
|
current_image: 0,
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|