ash-tray 0.19.0

A Tray to host Ash with Winit
//    Copyright 2019 Michael Mestnik

//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at

//        http://www.apache.org/licenses/LICENSE-2.0

//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

//! # Wrapper arround Rust bindings for dear imgui

use memoffset::offset_of;
use std::cell::RefCell;
use std::ffi::CStr;
use std::fmt::Debug;
use std::mem;
use std::ops::Deref;
use std::os::raw::c_uchar;
use std::rc::Rc;
use std::slice;

use crate::ash::vk;
use crate::imgui::{
    Context as ImGui, DrawIdx as ImDrawIdx, DrawVert as ImDrawVert, FontAtlasTexture, Ui,
};
use crate::vk_helper::{Error as VkHelperError, *};
use crate::vk_mem::{AllocationCreateFlags, AllocationCreateInfo, AllocatorCreateInfo};

use glsl_to_spirv_macros::{glsl_fs, glsl_vs};
use glsl_to_spirv_macros_impl::GLSLEmbedImpl;

use snafu::{ResultExt, Snafu};

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("vk_helper::Allocator creation error: {}", source))]
    VkHelperAllocatorNew { source: VkHelperError },
    #[snafu(display("vk_helper::ShaderModule vertex creation error: {}", source))]
    VkHelperVertexShaderModuleNew { source: VkHelperError },
    #[snafu(display("vk_helper::ShaderModule fragment creation error: {}", source))]
    VkHelperFragmentShaderModuleNew { source: VkHelperError },
    #[snafu(display("vk_helper::Sampler creation error: {}", source))]
    VkHelperSamplerNew { source: VkHelperError },
    #[snafu(display("vk_helper::DescriptorSetLayout creation error: {}", source))]
    VkHelperDescriptorSetLayoutNew { source: VkHelperError },
    #[snafu(display("vk_helper::PipelineLayout creation error: {}", source))]
    VkHelperPipelineLayoutNew { source: VkHelperError },
    #[snafu(display("vk_helper::Buffer creation error: {}", source))]
    VkHelperBufferNew { source: VkHelperError },
    #[snafu(display("vk_helper::Image creation error: {}", source))]
    VkHelperImageNew { source: VkHelperError },
    #[snafu(display("vk_helper::ImageView creation error: {}", source))]
    VkHelperImageViewNew { source: VkHelperError },
    #[snafu(display("vk_helper::DescriptorPool creation error: {}", source))]
    VkHelperDescriptorPoolNew { source: VkHelperError },
    #[snafu(display("vk_helper::DescriptorSet creation error: {}", source))]
    VkHelperDescriptorSetNew { source: VkHelperError },
    #[snafu(display("vk_helper::Pipeline creation error: {}", source))]
    VkHelperPipelineNew { source: VkHelperError },
    #[snafu(display("Pipeline not defined in ui renderer"))]
    PipelineNone,
    #[snafu(display("vk_helper::DescriptorSet write combined images error: {}", source))]
    VkHelperDescriptorSetWriteCombinedImages { source: VkHelperError },
    #[snafu(display("vk_helper::CommandBuffer bind descriptor sets error: {}", source))]
    VkHelperCommandBufferBindDescriptorSets { source: VkHelperError },
    #[snafu(display("vk_helper::Buffer flush vertex error: {}", source))]
    VkHelperBufferFlushVertex { source: VkHelperError },
    #[snafu(display("vk_helper::Buffer flush index error: {}", source))]
    VkHelperBufferFlushIndex { source: VkHelperError },
}

pub type Result<T> = std::result::Result<T, Error>;

pub trait UserDataTrait: Clone + Default + Debug {}
impl<T: Clone + Default + Debug> UserDataTrait for T {}
#[derive(Debug, Clone)]
pub struct Texture<T: UserDataTrait>(Rc<RcTexture<T>>);

impl<T: UserDataTrait> Deref for Texture<T> {
    type Target = RcTexture<T>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[derive(Debug)]
pub struct RcTexture<T: UserDataTrait> {
    pub sampler: Sampler<T>,
    pub image_view: ImageView<T>,
    pub image_layout: vk::ImageLayout,
}

pub struct Renderer<T: UserDataTrait> {
    pipeline_layout: PipelineLayout<T>,
    vertex_shader: ShaderModule<T>,
    fragment_shader: ShaderModule<T>,
    pipeline: RefCell<Option<Pipeline<T>>>,
    old_pipeline: RefCell<Option<Pipeline<T>>>,
    vertex_buffers: [Buffer<T>; 2],
    index_buffers: [Buffer<T>; 2],
    fonts_texture_sampler: Sampler<T>,
    fonts_texture_image_view: ImageView<T>,
    descriptor_set: DescriptorSet<T>,
    tex_ids: RefCell<Vec<Option<(usize, u8)>>>,
    frame_index: RefCell<usize>,
}

impl<T: UserDataTrait> Renderer<T> {
    const QUAD_COUNT_PER_FRAME: usize = 64 * 1024;
    const VERTEX_COUNT_PER_FRAME: usize = 4 * Self::QUAD_COUNT_PER_FRAME;
    const INDEX_COUNT_PER_FRAME: usize = 6 * Self::QUAD_COUNT_PER_FRAME;

    pub fn new_texture(&self, sampler: Sampler<T>, image: ImageView<T>) -> Texture<T> {
        Texture(Rc::new(RcTexture {
            sampler,
            image_view: image,
            image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
        }))
    }

    pub fn new_simple(
        instance: Instance<T>,
        physical_device: vk::PhysicalDevice,
        device: Device<T>,
        features: Option<vk::PhysicalDeviceVulkan12Features>,
        command_buffer: &CommandBuffer<T>,
        imgui: &mut ImGui,
    ) -> Result<Self> {
        let create_info =
            AllocatorCreateInfo::new(instance.inner.clone(), device.0.clone(), physical_device);
        let allocator = Allocator::new(
            instance,
            physical_device,
            device.clone(),
            create_info,
            Default::default(),
        )
        .context(VkHelperAllocatorNewSnafu {})?;
        Self::new(device, allocator, features, command_buffer, imgui)
    }

    pub fn new(
        device: Device<T>,
        allocator: Allocator<T>,
        features: Option<vk::PhysicalDeviceVulkan12Features>,
        command_buffer: &CommandBuffer<T>,
        imgui: &mut ImGui,
    ) -> Result<Self> {
        use arrayvec::ArrayVec;

        let vertex_shader = ShaderModule::new_simple(
            device.clone(),
            glsl_vs! { r#"#version 450 core
layout(location = 0) in vec2 aPos;
layout(location = 1) in vec2 aUV;
layout(location = 2) in vec4 aColor;
layout(push_constant) uniform uPushConstant { vec2 uScale; vec2 uTranslate; } pc;
out gl_PerVertex { vec4 gl_Position; };
layout(location = 0) out struct { vec4 Color; vec2 UV; } Out;
void main()
{
    Out.Color = aColor;
    Out.UV = aUV;
    gl_Position = vec4(aPos * pc.uScale + pc.uTranslate, 0, 1);
}"# },
            Default::default(),
        )
        .context(VkHelperVertexShaderModuleNewSnafu {})?;

        let fragment_shader = ShaderModule::new_simple(
            device.clone(),
            glsl_fs! { r#"#version 450 core
layout(location = 0) out vec4 fColor;
layout(push_constant) uniform uPushConstant {
    layout(offset=16) uint uTextureID;
} pc;
layout(set=0, binding=0) uniform sampler2D sTextures[30];
layout(location = 0) in struct { vec4 Color; vec2 UV; } In;
void main()
{
    fColor = In.Color * texture(sTextures[pc.uTextureID], In.UV.st);
}"# },
            Default::default(),
        )
        .context(VkHelperFragmentShaderModuleNewSnafu {})?;

        let bindings = vec![*vk::DescriptorSetLayoutBinding::builder()
            .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
            .descriptor_count(4096)
            .stage_flags(vk::ShaderStageFlags::FRAGMENT)];

        let descriptor_set_layout = if features
            .unwrap_or_default()
            .descriptor_binding_partially_bound
            == 0
        {
            DescriptorSetLayout::new(
                device.clone(),
                bindings,
                vk::DescriptorSetLayoutCreateInfo::builder(),
                Default::default(),
            )
        } else {
            let mut flags = vk::DescriptorSetLayoutBindingFlagsCreateInfo::builder()
                .binding_flags(&[vk::DescriptorBindingFlags::PARTIALLY_BOUND]);

            DescriptorSetLayout::new(
                device.clone(),
                bindings,
                vk::DescriptorSetLayoutCreateInfo::builder().push_next(&mut flags),
                Default::default(),
            )
        }
        .context(VkHelperDescriptorSetLayoutNewSnafu {})?;

        let push_constant_ranges = [
            *vk::PushConstantRange::builder()
                .stage_flags(vk::ShaderStageFlags::VERTEX)
                .size(16),
            *vk::PushConstantRange::builder()
                .stage_flags(vk::ShaderStageFlags::FRAGMENT)
                .offset(16)
                .size(4),
        ];

        let create_info =
            vk::PipelineLayoutCreateInfo::builder().push_constant_ranges(&push_constant_ranges);

        let pipeline_layout = PipelineLayout::new(
            device.clone(),
            vec![descriptor_set_layout.clone()],
            create_info,
            Default::default(),
        )
        .context(VkHelperPipelineLayoutNewSnafu {})?;

        let vertex_buffers = {
            let create_info = vk::BufferCreateInfo::builder()
                .size(
                    (Self::VERTEX_COUNT_PER_FRAME * mem::size_of::<ImDrawVert>()) as vk::DeviceSize,
                )
                .usage(vk::BufferUsageFlags::VERTEX_BUFFER);

            let mut buffers = ArrayVec::<Buffer<T>, 2>::new();
            for buffer in std::iter::repeat_with(|| {
                Buffer::new(
                    allocator.clone(),
                    &create_info,
                    &AllocationCreateInfo {
                        usage: crate::vk_mem::MemoryUsage::Auto,
                        flags: AllocationCreateFlags::MAPPED
                            | AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE,
                        ..Default::default()
                    },
                    Default::default(),
                )
            })
            .take(2)
            {
                buffers.push(buffer.context(VkHelperBufferNewSnafu)?);
            }
            buffers.into_inner().unwrap()
        };

        let index_buffers = {
            let create_info = vk::BufferCreateInfo::builder()
                .size((Self::INDEX_COUNT_PER_FRAME * mem::size_of::<ImDrawIdx>()) as vk::DeviceSize)
                .usage(vk::BufferUsageFlags::INDEX_BUFFER);

            let mut buffers = ArrayVec::<Buffer<T>, 2>::new();
            for buffer in std::iter::repeat_with(|| {
                Buffer::new(
                    allocator.clone(),
                    &create_info,
                    &AllocationCreateInfo {
                        usage: crate::vk_mem::MemoryUsage::Auto,
                        flags: AllocationCreateFlags::MAPPED
                            | AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE,
                        ..Default::default()
                    },
                    Default::default(),
                )
            })
            .take(2)
            {
                buffers.push(buffer.context(VkHelperBufferNewSnafu)?);
            }
            buffers.into_inner().unwrap()
        };

        let sampler = Sampler::new(
            device.clone(),
            &vk::SamplerCreateInfo::builder()
                .mag_filter(vk::Filter::LINEAR)
                .min_filter(vk::Filter::LINEAR),
            Default::default(),
        )
        .context(VkHelperSamplerNewSnafu {})?;

        let mut fonts = imgui.fonts();
        let image_view = {
            // Need to free fonts to edit it afterward.
            let FontAtlasTexture {
                width: image_width,
                height: image_height,
                data: image_pixels,
            } = fonts.build_rgba32_texture();

            let create_info = vk::BufferCreateInfo::builder()
                .size(vk::DeviceSize::from(image_width * image_height * 4))
                .usage(vk::BufferUsageFlags::TRANSFER_SRC);

            let image_buffer = Buffer::new(
                allocator.clone(),
                &create_info,
                &AllocationCreateInfo {
                    usage: crate::vk_mem::MemoryUsage::Auto,
                    flags: AllocationCreateFlags::MAPPED
                        | AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE,
                    ..Default::default()
                },
                Default::default(),
            )
            .context(VkHelperBufferNewSnafu {})?;

            let create_info = vk::ImageCreateInfo::builder()
                .image_type(vk::ImageType::TYPE_2D)
                .format(vk::Format::R8G8B8A8_UNORM)
                .extent(vk::Extent3D {
                    width: image_width,
                    height: image_height,
                    depth: 1,
                })
                .mip_levels(1)
                .array_layers(1)
                .samples(vk::SampleCountFlags::TYPE_1)
                .usage(vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST);

            let image = Image::new(
                allocator,
                &create_info,
                &AllocationCreateInfo {
                    usage: crate::vk_mem::MemoryUsage::Auto,
                    ..Default::default()
                },
                Default::default(),
            )
            .context(VkHelperImageNewSnafu {})?;

            let transfer_from_undef = vk::ImageMemoryBarrier::builder()
                .dst_access_mask(vk::AccessFlags::TRANSFER_WRITE)
                .new_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
                .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
                .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
                .subresource_range(
                    *vk::ImageSubresourceRange::builder()
                        .aspect_mask(vk::ImageAspectFlags::COLOR)
                        .level_count(1)
                        .layer_count(1),
                );
            command_buffer.pipeline_barrier(
                vk::PipelineStageFlags::HOST,
                vk::PipelineStageFlags::TRANSFER,
                vk::DependencyFlags::empty(),
                &[],
                vec![],
                vec![(image.clone(), transfer_from_undef)],
            );

            let image_base = unsafe {
                image_buffer
                    .allocator
                    .borrow_mut()
                    .get_allocation_info(&image_buffer.allocation)
            }
            .unwrap()
            .mapped_data as *mut c_uchar;
            assert_eq!(
                (image_width * image_height * 4) as usize,
                image_pixels.len()
            );
            unsafe {
                image_base.copy_from_nonoverlapping(image_pixels.as_ptr(), image_pixels.len());
            };

            let buffer_image_copy = vk::BufferImageCopy::builder()
                .image_subresource(
                    *vk::ImageSubresourceLayers::builder()
                        .aspect_mask(vk::ImageAspectFlags::COLOR)
                        .layer_count(1),
                )
                .image_extent(vk::Extent3D {
                    width: image_width,
                    height: image_height,
                    depth: 1,
                });
            command_buffer.copy_buffer_to_image(
                image_buffer,
                image.clone(),
                vk::ImageLayout::TRANSFER_DST_OPTIMAL,
                slice::from_ref(&*buffer_image_copy),
            );

            let shader_from_transfer = vk::ImageMemoryBarrier::builder()
                .src_access_mask(vk::AccessFlags::TRANSFER_WRITE)
                .dst_access_mask(vk::AccessFlags::SHADER_READ)
                .old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
                .new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
                .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
                .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
                .subresource_range(
                    *vk::ImageSubresourceRange::builder()
                        .aspect_mask(vk::ImageAspectFlags::COLOR)
                        .level_count(1)
                        .layer_count(1),
                );
            command_buffer.pipeline_barrier(
                vk::PipelineStageFlags::TRANSFER,
                vk::PipelineStageFlags::FRAGMENT_SHADER,
                vk::DependencyFlags::empty(),
                &[],
                vec![],
                vec![(image.clone(), shader_from_transfer)],
            );

            let create_info = vk::ImageViewCreateInfo::builder()
                .view_type(vk::ImageViewType::TYPE_2D)
                .format(vk::Format::R8G8B8A8_UNORM)
                .subresource_range(
                    *vk::ImageSubresourceRange::builder()
                        .aspect_mask(vk::ImageAspectFlags::COLOR)
                        .level_count(1)
                        .layer_count(1),
                );

            ImageView::new(image, create_info, Default::default())
                .context(VkHelperImageViewNewSnafu {})?
        };
        let (fonts_texture_sampler, fonts_texture_image_view) =
            (sampler.clone(), image_view.clone());
        let fonts_texture = Box::new(Texture(Rc::new(RcTexture {
            sampler,
            image_view,
            image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
        })));
        fonts.tex_id = (Box::leak(fonts_texture) as *const Texture<T>).into();

        let descriptor_pool_sizes = [*vk::DescriptorPoolSize::builder()
            .ty(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
            .descriptor_count(4096)];

        let create_info = vk::DescriptorPoolCreateInfo::builder()
            .flags(vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET)
            .max_sets(1)
            .pool_sizes(&descriptor_pool_sizes);

        let descriptor_pool = DescriptorPool::new(device, &create_info, Default::default())
            .context(VkHelperDescriptorPoolNewSnafu {})?;

        let descriptor_set = DescriptorSet::new(
            &descriptor_pool,
            vec![descriptor_set_layout],
            vk::DescriptorSetAllocateInfo::builder(),
            Default::default(),
            vec![],
        )
        .context(VkHelperDescriptorSetNewSnafu {})?[0]
            .clone();

        Ok(Self {
            pipeline_layout,
            vertex_shader,
            fragment_shader,
            pipeline: Default::default(),
            old_pipeline: Default::default(),
            vertex_buffers,
            index_buffers,
            fonts_texture_sampler,
            fonts_texture_image_view,
            descriptor_set,
            tex_ids: Default::default(),
            frame_index: Default::default(),
        })
    }

    pub fn begin_frame(&self) {
        if self.old_pipeline.borrow().is_some() {
            self.old_pipeline.replace(None);
        }
    }

    pub fn set_render_pass(&self, render_pass: RenderPass<T>) -> Result<()> {
        let mut shaders = std::collections::HashMap::new();
        shaders.insert(vk::ShaderStageFlags::VERTEX, self.vertex_shader.clone());
        shaders.insert(vk::ShaderStageFlags::FRAGMENT, self.fragment_shader.clone());

        let shader_entry_name = CStr::from_bytes_with_nul(b"main\0").unwrap();
        let shader_stage_create_infos = vec![
            vk::PipelineShaderStageCreateInfo::builder()
                .stage(vk::ShaderStageFlags::VERTEX)
                .name(shader_entry_name),
            vk::PipelineShaderStageCreateInfo::builder()
                .stage(vk::ShaderStageFlags::FRAGMENT)
                .name(shader_entry_name),
        ];

        let vertex_input_binding = vk::VertexInputBindingDescription::builder()
            .binding(0)
            .stride(mem::size_of::<ImDrawVert>() as u32)
            .input_rate(vk::VertexInputRate::VERTEX);
        #[allow(clippy::unneeded_field_pattern)]
        let vertex_input_attributes = [
            *vk::VertexInputAttributeDescription::builder()
                .location(0)
                .binding(0)
                .format(vk::Format::R32G32_SFLOAT)
                .offset(offset_of!(ImDrawVert, pos) as u32),
            *vk::VertexInputAttributeDescription::builder()
                .location(1)
                .binding(0)
                .format(vk::Format::R32G32_SFLOAT)
                .offset(offset_of!(ImDrawVert, uv) as u32),
            *vk::VertexInputAttributeDescription::builder()
                .location(2)
                .binding(0)
                .format(vk::Format::R8G8B8A8_UNORM)
                .offset(offset_of!(ImDrawVert, col) as u32),
        ];

        let vertex_input_state_create_info = vk::PipelineVertexInputStateCreateInfo::builder()
            .vertex_binding_descriptions(slice::from_ref(&vertex_input_binding))
            .vertex_attribute_descriptions(&vertex_input_attributes);

        let input_assembly_state_create_info = vk::PipelineInputAssemblyStateCreateInfo::builder()
            .topology(vk::PrimitiveTopology::TRIANGLE_LIST);

        let viewport = vk::Viewport::builder().build();
        let scissor = vk::Rect2D::builder().build();
        let viewport_state_create_info = vk::PipelineViewportStateCreateInfo::builder()
            .viewports(slice::from_ref(&viewport))
            .scissors(slice::from_ref(&scissor));

        let rasterization_state_create_info = vk::PipelineRasterizationStateCreateInfo::builder()
            .polygon_mode(vk::PolygonMode::FILL)
            .cull_mode(vk::CullModeFlags::NONE)
            .front_face(vk::FrontFace::CLOCKWISE)
            .line_width(1.);

        let depth_stencil_state_create_info = vk::PipelineDepthStencilStateCreateInfo::builder();

        let multisample_state_create_info = vk::PipelineMultisampleStateCreateInfo::builder()
            .rasterization_samples(vk::SampleCountFlags::TYPE_1);
        let color_blend_attachment_state = vk::PipelineColorBlendAttachmentState::builder()
            .blend_enable(true)
            .src_color_blend_factor(vk::BlendFactor::SRC_ALPHA)
            .dst_color_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
            .color_blend_op(vk::BlendOp::ADD)
            .src_alpha_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
            .dst_alpha_blend_factor(vk::BlendFactor::ZERO)
            .alpha_blend_op(vk::BlendOp::ADD)
            .color_write_mask(vk::ColorComponentFlags::RGBA);
        let color_blend_state_create_info = vk::PipelineColorBlendStateCreateInfo::builder()
            .attachments(slice::from_ref(&color_blend_attachment_state));

        let dynamic_states = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR];
        let pipeline_dynamic_state_create_info =
            vk::PipelineDynamicStateCreateInfo::builder().dynamic_states(&dynamic_states);

        let create_info = vk::GraphicsPipelineCreateInfo::builder()
            .vertex_input_state(&vertex_input_state_create_info)
            .input_assembly_state(&input_assembly_state_create_info)
            .viewport_state(&viewport_state_create_info)
            .rasterization_state(&rasterization_state_create_info)
            .depth_stencil_state(&depth_stencil_state_create_info)
            .multisample_state(&multisample_state_create_info)
            .color_blend_state(&color_blend_state_create_info)
            .dynamic_state(&pipeline_dynamic_state_create_info);

        self.old_pipeline.replace(Some(
            Pipeline::new(
                self.pipeline_layout.clone(),
                render_pass,
                shaders,
                shader_stage_create_infos,
                create_info,
                Default::default(),
            )
            .context(VkHelperPipelineNewSnafu {})?,
        ));
        self.old_pipeline.swap(&self.pipeline);
        Ok(())
    }

    pub fn render(&self, ui: Ui, command_buffer: &CommandBuffer<T>) -> Result<()> {
        let draw_data = ui.render();

        let x = draw_data.display_pos[0];
        let y = draw_data.display_pos[1];
        let width = draw_data.display_size[0] * draw_data.framebuffer_scale[0];
        let height = draw_data.display_size[1] * draw_data.framebuffer_scale[1];
        if !(width > 0. && height > 0.) {
            return Ok(());
        }

        command_buffer.bind_pipeline(
            self.pipeline
                .borrow()
                .as_ref()
                .ok_or(Error::PipelineNone)?
                .clone(),
        );

        let mut tex_ids = self.tex_ids.borrow_mut();
        tex_ids
            .iter()
            .filter_map(|&e| e)
            .for_each(|(_, ref mut age)| *age -= 1);
        let mut tex_ids_dirty = false;
        command_buffer
            .bind_descriptor_sets(0, vec![self.descriptor_set.clone()], &[])
            .context(VkHelperCommandBufferBindDescriptorSetsSnafu {})?;

        let scale = [width.recip() * 2., height.recip() * 2.];
        let translate = [x.mul_add(-scale[0], -1.), y.mul_add(-scale[1], -1.)];
        unsafe {
            command_buffer.command_pool.device.cmd_push_constants(
                ***command_buffer,
                **self.pipeline_layout,
                vk::ShaderStageFlags::VERTEX,
                0,
                std::slice::from_raw_parts([scale, translate].as_ptr() as *const u8, 16),
            );
        };

        let viewport = vk::Viewport::builder()
            .width(width)
            .height(height)
            .max_depth(1.);
        unsafe {
            command_buffer.command_pool.device.cmd_set_viewport(
                ***command_buffer,
                0,
                slice::from_ref(&*viewport),
            );
        };

        let vertex_buffer = self.vertex_buffers[*self.frame_index.borrow()].clone();
        let index_buffer = self.index_buffers[*self.frame_index.borrow()].clone();

        command_buffer.bind_vertex_buffers(0, vec![vertex_buffer.clone()], &[0]);
        command_buffer.bind_index_buffer(index_buffer.clone(), 0, vk::IndexType::UINT16);

        // ToDo: Work out allignment.
        #[allow(clippy::cast_ptr_alignment)]
        let vertex_base = unsafe {
            vertex_buffer
                .allocator
                .borrow_mut()
                .get_allocation_info(&vertex_buffer.allocation)
        }
        .unwrap()
        .mapped_data as *mut ImDrawVert;
        #[allow(clippy::cast_ptr_alignment)]
        let index_base = unsafe {
            index_buffer
                .allocator
                .borrow_mut()
                .get_allocation_info(&index_buffer.allocation)
        }
        .unwrap()
        .mapped_data as *mut ImDrawIdx;
        let mut global_vertex_offset = 0;
        let mut global_index_offset = 0;
        let mut current_texture_id = None;
        for draw_list in draw_data.draw_lists() {
            let vtx_buffer = draw_list.vtx_buffer();
            let idx_buffer = draw_list.idx_buffer();

            let next_vertex_offset = global_vertex_offset + vtx_buffer.len();
            let next_index_offset = global_index_offset + idx_buffer.len();
            if next_vertex_offset > Self::VERTEX_COUNT_PER_FRAME
                || next_index_offset > Self::INDEX_COUNT_PER_FRAME
            {
                break;
            }

            unsafe {
                vertex_base
                    .add(global_vertex_offset)
                    .copy_from_nonoverlapping(vtx_buffer.as_ptr(), vtx_buffer.len());
                index_base
                    .add(global_index_offset)
                    .copy_from_nonoverlapping(idx_buffer.as_ptr(), idx_buffer.len());
            }
            vertex_buffer
                .flush(
                    global_vertex_offset * mem::size_of::<ImDrawVert>(),
                    vtx_buffer.len() * mem::size_of::<ImDrawVert>(),
                )
                .context(VkHelperBufferFlushVertexSnafu {})?;
            index_buffer
                .flush(
                    global_index_offset * mem::size_of::<u16>(),
                    idx_buffer.len() * mem::size_of::<u16>(),
                )
                .context(VkHelperBufferFlushIndexSnafu {})?;

            let clip_off = draw_data.display_pos;
            let clip_scale = [1., 1.]; // draw_data.framebuffer_scale;
            for cmd in draw_list.commands() {
                match cmd {
                    crate::imgui::DrawCmd::Elements {
                        count,
                        cmd_params:
                            crate::imgui::DrawCmdParams {
                                clip_rect,
                                texture_id,
                                vtx_offset: _,
                                idx_offset: _,
                            },
                    } => {
                        let offset = vk::Offset2D::builder()
                            .x((((clip_rect[0] - clip_off[0]) * clip_scale[0]) as i32).max(0))
                            .y((((clip_rect[1] - clip_off[1]) * clip_scale[1]) as i32).max(0));
                        let extent = vk::Extent2D::builder()
                            .width(
                                (((clip_rect[2] - clip_off[0]) * clip_scale[0]) as i32 - offset.x)
                                    as u32,
                            )
                            .height(
                                (((clip_rect[3] - clip_off[1]) * clip_scale[1]) as i32 - offset.y)
                                    as u32,
                            );
                        if offset.x < width as i32
                            && offset.y < height as i32
                            && extent.width as i32 >= -offset.x
                            && extent.height as i32 >= -offset.y
                        {
                            let texture_id = texture_id.id();
                            let scissor = vk::Rect2D::builder().offset(*offset).extent(*extent);
                            unsafe {
                                command_buffer.command_pool.device.cmd_set_scissor(
                                    ***command_buffer,
                                    0,
                                    slice::from_ref(&scissor),
                                );
                            };
                            if current_texture_id != Some(texture_id) {
                                let index = tex_ids
                                    .iter_mut()
                                    .enumerate()
                                    .filter_map(|(i, x)| x.as_mut().map(|(x, age)| (i, *x, age)))
                                    .find_map(|(i, x, age)| {
                                        if texture_id == x {
                                            *age = 5;
                                            Some(i)
                                        } else {
                                            None
                                        }
                                    })
                                    .unwrap_or_else(|| {
                                        tex_ids_dirty = true;
                                        let add_me = Some((texture_id, 5));
                                        if let Some((id, object)) = tex_ids
                                            .iter_mut()
                                            .enumerate()
                                            .find(|(_, x)| x.is_none())
                                        {
                                            *object = add_me;
                                            id
                                        } else {
                                            let ret = tex_ids.len() as _;
                                            tex_ids.push(add_me);
                                            ret
                                        }
                                    })
                                    .to_ne_bytes();
                                unsafe {
                                    command_buffer.command_pool.device.cmd_push_constants(
                                        ***command_buffer,
                                        **self.pipeline_layout,
                                        vk::ShaderStageFlags::FRAGMENT,
                                        16,
                                        &index,
                                    )
                                };
                                current_texture_id = Some(texture_id);
                            }
                            unsafe {
                                command_buffer.command_pool.device.cmd_draw_indexed(
                                    ***command_buffer,
                                    count as u32,
                                    1,
                                    (global_index_offset) as u32,
                                    (global_vertex_offset) as i32,
                                    0,
                                );
                            };
                        }
                        global_index_offset += count;
                    }
                    crate::imgui::DrawCmd::ResetRenderState => (), // TODO
                    crate::imgui::DrawCmd::RawCallback { .. } => (), // TODO
                };
            }
            global_vertex_offset = next_vertex_offset;
            assert_eq!(global_index_offset, next_index_offset);
        }

        if tex_ids_dirty {
            let images = tex_ids
                .iter_mut()
                .map(|x| match x {
                    Some((_, 0)) => {
                        *x = None;
                        (
                            self.fonts_texture_sampler.clone(),
                            self.fonts_texture_image_view.clone(),
                            vk::DescriptorImageInfo::builder()
                                .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL),
                        )
                    }
                    None => (
                        self.fonts_texture_sampler.clone(),
                        self.fonts_texture_image_view.clone(),
                        vk::DescriptorImageInfo::builder()
                            .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL),
                    ),
                    Some((texture_id, _)) => unsafe {
                        let texture = *texture_id as *const Texture<T>;
                        (
                            (*texture).sampler.clone(),
                            (*texture).image_view.clone(),
                            vk::DescriptorImageInfo::builder()
                                .image_layout((*texture).image_layout),
                        )
                    },
                })
                .collect::<Vec<_>>();
            self.descriptor_set
                .write_combined_images(images, vk::WriteDescriptorSet::builder())
                .context(VkHelperDescriptorSetWriteCombinedImagesSnafu {})?;
        }

        self.frame_index
            .replace_with(|frame_index| (1 + *frame_index) % 2);

        Ok(())
    }
}