#![deny(unsafe_code)]
use crate::error::*;
use crate::graphics::{ Permanent, ForReinit, WindowDressing, Texture };
use crate::graphics::util::allocate_buffer;
use crate::shader_data::UniformBlock;
use std::mem::size_of;
use vulkanalia::{ Device, Instance };
use vulkanalia::vk::{ self, HasBuilder, DeviceV1_0 };
// Frame is a state object that collects the Vulkan graphics objects which
// are used as part of rendering and which need to exist in multiples, one for
// each frame that can be rendered in parallel (see N_SIMULTANEOUS_FRAMES in
// window_dressing.rs). As with WindowDressing, these need to be regenerated
// or modified when the window changes.
#[derive(Debug)]
pub struct Frame {
pub framebuffer: vk::Framebuffer,
pub command_buffer: vk::CommandBuffer,
// Uniform buffers are unlike the other things in Frame in that they are
// not used at render-time. It's still cleanest to have them here, since
// they exist per-frame. Otherwise we get action-at-a-distance passing
// around indices, which is harder to trace through.
pub uniform_buffer: vk::Buffer,
pub uniform_buffer_memory: vk::DeviceMemory,
pub descriptor_set: vk::DescriptorSet,
}
impl Frame {
// The lifecycle stuff for Frame is a little different. The Vulkan API to
// allocate and deallocate command buffers is designed on the assumption you
// want to handle a few of them simultaneously. That is in fact what we want,
// so the interfaces to new() and reinit() work on Vec instead of on
// an indidivual Frame.
#[allow(unsafe_code)]
pub fn new(permanent: &Permanent, for_reinit: &ForReinit,
window_dressing: &WindowDressing, texture: &Texture,
render_pass: &vk::RenderPass)
-> Result>
{
let mut frames = Vec::new();
Frame::reinit(&mut frames, permanent, for_reinit, window_dressing,
texture, render_pass)?;
Ok(frames)
}
// See new() in regard to the Vec.
#[allow(unsafe_code)]
pub fn reinit(frames: &mut Vec, permanent: &Permanent,
for_reinit: &ForReinit, window_dressing: &WindowDressing,
texture: &Texture, render_pass: &vk::RenderPass)
-> Result<()>
{
Frame::destroy_replaceable(frames, permanent);
let instance = &permanent.instance;
let device = &permanent.device;
let primary_command_pool = &permanent.primary_command_pool;
let descriptor_set_layout = &for_reinit.descriptor_set_layout;
let swapchain = &window_dressing.swapchain;
let color_image_view = &window_dressing.color_image_view;
let depth_image_view = &window_dressing.depth_image_view;
let descriptor_pool = &window_dressing.descriptor_pool;
let sampler = &window_dressing.sampler;
frames.clear();
let count = swapchain.image_views.len();
// Notice that we reused the command pool.
let command_buffers = allocate_command_buffers(count, device,
primary_command_pool)?;
let descriptor_sets = allocate_descriptor_sets(
count, device, descriptor_set_layout, descriptor_pool)?;
for (index, color_resolve_image_view)
in swapchain.image_views.iter().enumerate()
{
let command_buffer = command_buffers[index];
let mut descriptor_set = descriptor_sets[index];
let framebuffer = init_framebuffer(
device, &swapchain.extent, &color_image_view, &depth_image_view,
color_resolve_image_view, &render_pass)?;
let (uniform_buffer, uniform_buffer_memory)
= init_uniform_buffer(instance, device)?;
configure_descriptor_set(&mut descriptor_set, device, &uniform_buffer,
&texture.image_view, sampler)?;
frames.push(Frame {
framebuffer, command_buffer, uniform_buffer, uniform_buffer_memory,
descriptor_set,
});
}
Ok(())
}
// This relies on its caller to have already waited for the device to be
// idle.
#[allow(unsafe_code)]
pub fn destroy(frames: &mut Vec, permanent: &Permanent) {
Frame::destroy_replaceable(frames, permanent);
}
#[allow(unsafe_code)]
pub fn destroy_replaceable(frames: &mut Vec, permanent: &Permanent) {
if frames.is_empty() {
return;
}
let device = &permanent.device;
let mut command_buffers = Vec::new();
for frame in frames {
unsafe { device.destroy_framebuffer(frame.framebuffer, None) };
command_buffers.push(frame.command_buffer);
unsafe { device.destroy_buffer(frame.uniform_buffer, None) };
unsafe { device.free_memory(frame.uniform_buffer_memory, None) };
}
// Notice that we free the buffers in the pool, but do not destroy the
// pool itself. Notice also that we only do this for the primary command
// pool, because that's the only one where we've kept track of the
// buffers. We promise ourselves to free buffers in the transient pool
// immediately after using them.
unsafe {
device.free_command_buffers(permanent.primary_command_pool,
&command_buffers)
};
}
}
#[allow(unsafe_code)]
fn init_framebuffer(device: &Device, extent: &vk::Extent2D,
color_image_view: &vk::ImageView,
depth_image_view: &vk::ImageView,
color_resolve_image_view: &vk::ImageView,
render_pass: &vk::RenderPass)
-> Result
{
let attachments = [*color_image_view,
*depth_image_view,
*color_resolve_image_view];
let framebuffer_info = vk::FramebufferCreateInfo::builder()
.render_pass(*render_pass)
.attachments(&attachments)
.width(extent.width)
.height(extent.height)
.layers(1);
let framebuffer = unsafe {
device.create_framebuffer(&framebuffer_info, None)
}?;
Ok(framebuffer)
}
#[allow(unsafe_code)]
fn allocate_command_buffers(count: usize, device: &Device,
command_pool: &vk::CommandPool)
-> Result>
{
let command_buffer_allocation_info
= vk::CommandBufferAllocateInfo::builder()
.command_pool(*command_pool)
.level(vk::CommandBufferLevel::PRIMARY)
.command_buffer_count(count as u32);
let command_buffers = unsafe {
device.allocate_command_buffers(&command_buffer_allocation_info)
}?;
Ok(command_buffers)
}
#[allow(unsafe_code)]
fn allocate_descriptor_sets(count: usize, device: &Device,
descriptor_set_layout: &vk::DescriptorSetLayout,
descriptor_pool: &vk::DescriptorPool)
-> Result>
{
let layouts = vec![*descriptor_set_layout; count];
let descriptor_set_info = vk::DescriptorSetAllocateInfo::builder()
.descriptor_pool(*descriptor_pool)
.set_layouts(&layouts[..]);
let descriptor_sets = unsafe {
device.allocate_descriptor_sets(&descriptor_set_info)
}?;
Ok(descriptor_sets)
}
#[allow(unsafe_code)]
fn configure_descriptor_set(descriptor_set: &mut vk::DescriptorSet,
device: &Device,
uniform_buffer: &vk::Buffer,
texture_image_view: &vk::ImageView,
sampler: &vk::Sampler)
-> Result<()>
{
let buffer_info = vk::DescriptorBufferInfo::builder()
.buffer(*uniform_buffer)
.offset(0)
.range(size_of::>() as vk::DeviceSize);
let buffer_info_list = [buffer_info];
let uniform_block_write_info = vk::WriteDescriptorSet::builder()
.dst_set(*descriptor_set)
.dst_binding(0)
.dst_array_element(0)
.descriptor_type(vk::DescriptorType::UNIFORM_BUFFER)
.buffer_info(&buffer_info_list);
let sampler_image_info = vk::DescriptorImageInfo::builder()
.sampler(*sampler);
let sampler_image_info_list = [sampler_image_info];
let sampler_write_info = vk::WriteDescriptorSet::builder()
.dst_set(*descriptor_set)
.dst_binding(1)
.dst_array_element(0)
.descriptor_type(vk::DescriptorType::SAMPLER)
.image_info(&sampler_image_info_list);
let texture_image_info = vk::DescriptorImageInfo::builder()
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
.image_view(*texture_image_view);
let texture_image_info_list = [texture_image_info];
let texture_write_info = vk::WriteDescriptorSet::builder()
.dst_set(*descriptor_set)
.dst_binding(2)
.dst_array_element(0)
.descriptor_type(vk::DescriptorType::SAMPLED_IMAGE)
.image_info(&texture_image_info_list);
let write_info_list = [
uniform_block_write_info, sampler_write_info, texture_write_info
];
let copy_info_list: [vk::CopyDescriptorSet; 0] = [];
unsafe {
device.update_descriptor_sets(&write_info_list, ©_info_list)
};
Ok(())
}
fn init_uniform_buffer(instance: &Instance, device: &Device)
-> Result<(vk::Buffer, vk::DeviceMemory)>
{
let (buffer, memory) = allocate_buffer(
instance, device,
size_of::>() as vk::DeviceSize,
vk::BufferUsageFlags::UNIFORM_BUFFER,
vk::MemoryPropertyFlags::HOST_COHERENT
| vk::MemoryPropertyFlags::HOST_VISIBLE)?;
Ok((buffer, memory))
}