#![allow(unused_variables, unused_mut)]

use ash::Device;
use ash::Entry;
use ash::Instance;
use ash::khr::{surface, swapchain};
use ash::vk;
use ash::vk::ImageCreateFlags;
use sdl2::mouse::MouseUtil;

use std::cmp;
use std::ffi::CStr;
use std::ffi::CString;
use std::thread;
use std::time::Duration;
use std::time::Instant;

use raw_window_handle::HasDisplayHandle;
use raw_window_handle::HasWindowHandle;

use sdl2::event::Event;
use sdl2::event::WindowEvent;
use sdl2::keyboard::Keycode;
use sdl2::video::Window;

const APP_NAME: &str = "MineClone";

const MOV_STEP: f32 = 0.05;
const ROT_MOUSE_SCALE: f32 = 0.001;

const MAX_WIDTH: u32 = 3440;
const MAX_HEIGHT: u32 = 1440;

const MESH_SIZE: u64 = 36 * 2 * 4 * 4;

#[rustfmt::skip]
const POSITIONS: [f32; 36 * 2 * 4] = [
    // BOTTOM

    -0.5, 0.5, -0.5, 1.0,
    0.0, 1.0, 0.0, 0.0,

    0.5, 0.5, 0.5, 1.0,
    0.0, 1.0, 0.0, 0.0,

    -0.5, 0.5, 0.5, 1.0,
    0.0, 1.0, 0.0, 0.0,

    0.5, 0.5, -0.5, 1.0,
    0.0, 1.0, 0.0, 0.0,

    0.5, 0.5, 0.5, 1.0,
    0.0, 1.0, 0.0, 0.0,

    -0.5, 0.5, -0.5, 1.0,
    0.0, 1.0, 0.0, 0.0,

    // TOP
    0.5, -0.5, 0.5, 1.0,
    0.0, -1.0, 0.0, 0.0,

    -0.5, -0.5, -0.5, 1.0,
    0.0, -1.0, 0.0, 0.0,

    -0.5, -0.5, 0.5, 1.0,
    0.0, -1.0, 0.0, 0.0,

    0.5, -0.5, 0.5, 1.0,
    0.0, -1.0, 0.0, 0.0,

    0.5, -0.5, -0.5, 1.0,
    0.0, -1.0, 0.0, 0.0,

    -0.5, -0.5, -0.5, 1.0,
    0.0, -1.0, 0.0, 0.0,

    // FRONT
    -0.5, -0.5, -0.5, 1.0,
    0.0, 0.0, -1.0, 0.0,

    0.5, 0.5, -0.5, 1.0,
    0.0, 0.0, -1.0, 0.0,

    -0.5, 0.5, -0.5, 1.0,
    0.0, 0.0, -1.0, 0.0,

    0.5, -0.5, -0.5, 1.0,
    0.0, 0.0, -1.0, 0.0,

    0.5, 0.5, -0.5, 1.0,
    0.0, 0.0, -1.0, 0.0,

    -0.5, -0.5, -0.5, 1.0,
    0.0, 0.0, -1.0, 0.0,

    // BACK
    0.5, 0.5, 0.5, 1.0,
    0.0, 0.0, 1.0, 0.0,

    -0.5, -0.5, 0.5, 1.0,
    0.0, 0.0, 1.0, 0.0,

    -0.5, 0.5, 0.5, 1.0,
    0.0, 0.0, 1.0, 0.0,

    0.5, 0.5, 0.5, 1.0,
    0.0, 0.0, 1.0, 0.0,

    0.5, -0.5, 0.5, 1.0,
    0.0, 0.0, 1.0, 0.0,

    -0.5, -0.5, 0.5, 1.0,
    0.0, 0.0, 1.0, 0.0,

    // LEFT
    -0.5, -0.5, -0.5, 1.0,
    -1.0, 0.0, 0.0, 0.0,

    -0.5, 0.5, 0.5, 1.0,
    -1.0, 0.0, 0.0, 0.0,

    -0.5, -0.5, 0.5, 1.0,
    -1.0, 0.0, 0.0, 0.0,

    -0.5, 0.5, -0.5, 1.0,
    -1.0, 0.0, 0.0, 0.0,

    -0.5, 0.5, 0.5, 1.0,
    -1.0, 0.0, 0.0, 0.0,

    -0.5, -0.5, -0.5, 1.0,
    -1.0, 0.0, 0.0, 0.0,

    // RIGHT
    0.5, 0.5, 0.5, 1.0,
    1.0, 0.0, 0.0, 0.0,
    0.5, -0.5, -0.5, 1.0,
    1.0, 0.0, 0.0, 0.0,

    0.5, -0.5, 0.5, 1.0,
    1.0, 0.0, 0.0, 0.0,

    0.5, 0.5, 0.5, 1.0,
    1.0, 0.0, 0.0, 0.0,

    0.5, 0.5, -0.5, 1.0,
    1.0, 0.0, 0.0, 0.0,

    0.5, -0.5, -0.5, 1.0,
    1.0, 0.0, 0.0, 0.0,


];

fn create_instance(window: &Window, entry: &Entry) -> Instance {
    unsafe {
        let n_app_name = CString::new(APP_NAME).unwrap();
        let n_engine_name = CString::new(format!("{} Engine", APP_NAME)).unwrap();

        let appinfo = vk::ApplicationInfo::default()
            .application_name(n_app_name.as_c_str())
            .application_version(0)
            .engine_name(n_engine_name.as_c_str())
            .engine_version(0)
            .api_version(vk::make_api_version(0, 1, 3, 0));

        let default_exts = window
            .vulkan_instance_extensions()
            .expect("Failed to get list of required extensions from SDL2");

        let mut exts = vec![];
        exts.extend(default_exts);

        let exts: Vec<*const i8> = exts
            .iter()
            .map(|s| s.as_ptr() as *const i8)
            .collect::<Vec<_>>();
        let exts: &[*const i8] = exts.as_slice();

        let insinfo = vk::InstanceCreateInfo::default()
            .application_info(&appinfo)
            .enabled_extension_names(exts);

        let instance = entry
            .create_instance(&insinfo, None)
            .expect("Failed to create instance");
        println!("Created instance");
        instance
    }
}

fn find_pdev_dgpu(instance: &Instance) -> vk::PhysicalDevice {
    unsafe {
        let mut pdevs = instance
            .enumerate_physical_devices()
            .expect("Failed to enumerate devices");
        for (i, d) in pdevs.iter().enumerate() {
            let props = instance.get_physical_device_properties(*d);
            if props.device_type == vk::PhysicalDeviceType::DISCRETE_GPU {
                // TODO this assumes that the first discrete GPU will have an RCS queue family
                // This is not guaranteed by the spec (example, a compute-only GPU)
                // Fix.
                println!(
                    "Found discrete GPU: {}",
                    std::str::from_utf8(std::mem::transmute(props.device_name.as_slice())).unwrap()
                );
                return pdevs.remove(i);
            }
        }
        println!("No discrete GPU found");
        assert!(pdevs.len() > 0, "No GPU found");
        return pdevs.remove(0);
    }
}

fn make_surface(entry: &Entry, instance: &Instance, window: &Window) -> vk::SurfaceKHR {
    unsafe {
        let surface = ash_window::create_surface(
            &entry,
            &instance,
            (&window).display_handle().unwrap().as_raw(),
            (&window).window_handle().unwrap().as_raw(),
            None,
        )
        .expect("Failed to create surface");
        println!("Created surface");
        surface
    }
}

// Using Intel GPU hardware terminology 🥴
fn find_rcs_queue_inx(instance: &Instance, pdev: &vk::PhysicalDevice) -> Option<u32> {
    unsafe {
        let qfams = instance.get_physical_device_queue_family_properties(*pdev);
        for (inx, qfam) in qfams.iter().enumerate() {
            if qfam
                .queue_flags
                .contains(vk::QueueFlags::GRAPHICS | vk::QueueFlags::COMPUTE)
            {
                println!("Found RCS queue at index {}", inx);
                return Some(inx as u32);
            }
        }
        println!("No RCS queue found");
        return None;
    }
}

fn make_dev(
    instance: &Instance,
    pdev: &vk::PhysicalDevice,
    rcs_queue_inx: u32,
    exts: Vec<&CStr>,
) -> Device {
    unsafe {
        let dev_queue = vec![
            vk::DeviceQueueCreateInfo::default()
                .queue_family_index(rcs_queue_inx)
                .queue_priorities(&[1.0]),
        ];

        let exts = exts.iter().map(|&s| s.as_ptr()).collect::<Vec<_>>();
        let exts = exts.as_slice();

        // Enable all features
        let features = instance.get_physical_device_features(*pdev);

        let dev_info = vk::DeviceCreateInfo::default()
            .queue_create_infos(&dev_queue)
            .enabled_extension_names(exts)
            .enabled_features(&features);

        let dev = instance
            .create_device(*pdev, &dev_info, None)
            .expect("Failed to create device");
        println!("Created device");
        dev
    }
}

fn make_command_pool(dev: &Device, rcs_queue_inx: u32) -> vk::CommandPool {
    unsafe {
        let pool_info = vk::CommandPoolCreateInfo::default()
            .queue_family_index(rcs_queue_inx)
            .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER);
        let pool = dev
            .create_command_pool(&pool_info, None)
            .expect("Failed to create command pool");
        println!("Created command pool");
        pool
    }
}

fn alloc_cmd_buf(dev: &Device, pool: vk::CommandPool) -> vk::CommandBuffer {
    unsafe {
        let alloc_info = vk::CommandBufferAllocateInfo::default()
            .command_pool(pool)
            .level(vk::CommandBufferLevel::PRIMARY)
            .command_buffer_count(1);
        let cmd_buf = dev
            .allocate_command_buffers(&alloc_info)
            .expect("Failed to allocate command buffer")
            .remove(0);
        println!("Allocated command buffer");
        cmd_buf
    }
}

fn find_host_visible(instance: &Instance, pdev: &vk::PhysicalDevice) -> Option<u32> {
    unsafe {
        let mem_props = instance.get_physical_device_memory_properties(*pdev);
        for (i, mem_type) in mem_props.memory_types.iter().enumerate() {
            if mem_type.property_flags.contains(
                vk::MemoryPropertyFlags::DEVICE_LOCAL
                    | vk::MemoryPropertyFlags::HOST_VISIBLE
                    | vk::MemoryPropertyFlags::HOST_COHERENT,
            ) {
                println!("Found host visible memory at index {}", i);
                return Some(i as u32);
            }
        }
        println!("No host visible memory found");
        return None;
    }
}

fn find_host_invisible(instance: &Instance, pdev: &vk::PhysicalDevice) -> Option<u32> {
    unsafe {
        let mem_props = instance.get_physical_device_memory_properties(*pdev);
        for (i, mem_type) in mem_props.memory_types.iter().enumerate() {
            if mem_type
                .property_flags
                .contains(vk::MemoryPropertyFlags::DEVICE_LOCAL)
                && !mem_type.property_flags.contains(
                    vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
                )
            {
                println!("Found host invisible memory at index {}", i);
                return Some(i as u32);
            }
        }
        println!("No host invisible memory found");
        return None;
    }
}

fn mem_alloc(dev: &Device, mem_type_inx: u32, size: u64) -> vk::DeviceMemory {
    unsafe {
        let mem_info = vk::MemoryAllocateInfo::default()
            .allocation_size(size)
            .memory_type_index(mem_type_inx);
        let mem = dev
            .allocate_memory(&mem_info, None)
            .expect("Failed to allocate memory");
        println!("Allocated memory");
        mem
    }
}

fn make_depth_img(
    dev: &Device,
    host_invisible_inx: u32,
    width: u32,
    height: u32,
    mem: vk::DeviceMemory,
    qfam: u32,
) -> vk::Image {
    unsafe {
        let queue_fams = [qfam];
        let img_info = vk::ImageCreateInfo::default()
            .flags(vk::ImageCreateFlags::empty())
            .image_type(vk::ImageType::TYPE_2D)
            .format(vk::Format::D32_SFLOAT)
            .extent(vk::Extent3D {
                width,
                height,
                depth: 1,
            })
            .mip_levels(1)
            .array_layers(1)
            .samples(vk::SampleCountFlags::TYPE_1)
            .tiling(vk::ImageTiling::OPTIMAL)
            .usage(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT)
            .sharing_mode(vk::SharingMode::EXCLUSIVE)
            .queue_family_indices(&queue_fams)
            .initial_layout(vk::ImageLayout::UNDEFINED);

        let img = dev
            .create_image(&img_info, None)
            .expect("Failed to create image");
        println!("Created image");

        dev.bind_image_memory(img, mem, 0)
            .expect("Failed to bind image memory");

        img
    }
}

fn alloc_buf(
    dev: &Device,
    mem: &vk::DeviceMemory,
    size: u64,
    usage: vk::BufferUsageFlags,
) -> vk::Buffer {
    unsafe {
        let buf_info = vk::BufferCreateInfo::default()
            .size(size)
            .usage(usage)
            .sharing_mode(vk::SharingMode::EXCLUSIVE);
        let buf = dev
            .create_buffer(&buf_info, None)
            .expect("Failed to create buffer");

        dev.bind_buffer_memory(buf, *mem, 0)
            .expect("Failed to bind buffer memory");
        println!("Created buffer");
        buf
    }
}

fn buf_to_ptr(dev: &Device, mem: &vk::DeviceMemory, buf: &vk::Buffer) -> *mut std::ffi::c_void {
    unsafe {
        let ptr = dev
            .map_memory(*mem, 0, vk::WHOLE_SIZE, vk::MemoryMapFlags::empty())
            .expect("Failed to map memory");
        println!("Mapped memory");
        ptr
    }
}

fn setup_desc_layout(dev: &Device) -> vk::DescriptorSetLayout {
    let storage_binding = vk::DescriptorSetLayoutBinding::default()
        .binding(0)
        .descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
        .descriptor_count(1)
        .stage_flags(vk::ShaderStageFlags::VERTEX);
    let image_binding = vk::DescriptorSetLayoutBinding::default()
        .binding(1)
        .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
        .descriptor_count(1)
        .stage_flags(vk::ShaderStageFlags::FRAGMENT);
    let layouts = [storage_binding, image_binding];

    let layout_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&layouts);
    let layout = unsafe {
        dev.create_descriptor_set_layout(&layout_info, None)
            .expect("Failed to create descriptor set layout")
    };
    layout
}

fn setup_render_pass(dev: &Device) -> vk::RenderPass {
    let color_attach = vk::AttachmentDescription::default()
        .format(vk::Format::B8G8R8A8_UNORM)
        .samples(vk::SampleCountFlags::TYPE_1)
        .load_op(vk::AttachmentLoadOp::CLEAR)
        .store_op(vk::AttachmentStoreOp::STORE)
        .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
        .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
        .initial_layout(vk::ImageLayout::UNDEFINED)
        .final_layout(vk::ImageLayout::PRESENT_SRC_KHR);
    let depth_attach = vk::AttachmentDescription::default()
        .format(vk::Format::D32_SFLOAT)
        .samples(vk::SampleCountFlags::TYPE_1)
        .load_op(vk::AttachmentLoadOp::CLEAR)
        .store_op(vk::AttachmentStoreOp::DONT_CARE)
        .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
        .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
        .initial_layout(vk::ImageLayout::UNDEFINED)
        .final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL);

    let color_ref = vk::AttachmentReference::default()
        .attachment(0)
        .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);

    let depth_ref = vk::AttachmentReference::default()
        .attachment(1)
        .layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL);

    let color_refs = [color_ref];
    let depth_refs = [depth_ref];

    let subpass = vk::SubpassDescription::default()
        .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
        .color_attachments(color_refs.as_ref())
        .depth_stencil_attachment(&depth_ref);

    let subpass_dep = vk::SubpassDependency::default()
        .src_subpass(vk::SUBPASS_EXTERNAL)
        .dst_subpass(0)
        .src_stage_mask(
            vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
                | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS,
        )
        .dst_stage_mask(
            vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
                | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS,
        )
        .src_access_mask(vk::AccessFlags::empty())
        .dst_access_mask(
            vk::AccessFlags::COLOR_ATTACHMENT_WRITE
                | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
        );

    let attach_desc = [color_attach, depth_attach];

    let subpasses = [subpass];

    let deps = [subpass_dep];

    let pass_info = vk::RenderPassCreateInfo::default()
        .attachments(&attach_desc)
        .subpasses(subpasses.as_ref())
        .dependencies(deps.as_ref());

    let pass = unsafe {
        dev.create_render_pass(&pass_info, None)
            .expect("Failed to create render pass")
    };
    pass
}

fn setup_pipe_layout(dev: &Device) -> vk::PipelineLayout {
    let layout = setup_desc_layout(&dev);
    let push_range: [vk::PushConstantRange; 2] = [
        vk::PushConstantRange::default()
            .stage_flags(vk::ShaderStageFlags::VERTEX)
            .offset(0)
            .size(48), // vec4 camera_orig, camera_rot
        vk::PushConstantRange::default()
            .stage_flags(vk::ShaderStageFlags::FRAGMENT)
            .offset(48)
            .size(16), // vec4 base_color
    ];

    let layouts = &[layout];

    let pipe_layout = vk::PipelineLayoutCreateInfo::default()
        .set_layouts(layouts.as_ref())
        .push_constant_ranges(&push_range);
    let pipe_layout = unsafe {
        dev.create_pipeline_layout(&pipe_layout, None)
            .expect("Failed to create pipeline layout")
    };
    pipe_layout
}

fn setup_shader_stage(
    dev: &Device,
    vert_shader: vk::ShaderModule,
    frag_shader: vk::ShaderModule,
) -> [vk::PipelineShaderStageCreateInfo; 2] {
    let vert_stage = vk::PipelineShaderStageCreateInfo::default()
        .stage(vk::ShaderStageFlags::VERTEX)
        .module(vert_shader)
        .name(c"main");
    let frag_stage = vk::PipelineShaderStageCreateInfo::default()
        .stage(vk::ShaderStageFlags::FRAGMENT)
        .module(frag_shader)
        .name(c"main");
    [vert_stage, frag_stage]
}

fn setup_pipeline(
    dev: &Device,
    vert_shader: &vk::ShaderModule,
    frag_shader: &vk::ShaderModule,
    pass: &vk::RenderPass,
    pipe_layout: &vk::PipelineLayout,
) -> vk::Pipeline {
    let shader_stages = setup_shader_stage(&dev, *vert_shader, *frag_shader);
    let vert_input = vk::PipelineVertexInputStateCreateInfo::default();
    let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default()
        .topology(vk::PrimitiveTopology::TRIANGLE_LIST);
    let rasterization = vk::PipelineRasterizationStateCreateInfo::default()
        .polygon_mode(vk::PolygonMode::FILL)
        .cull_mode(vk::CullModeFlags::NONE)
        .front_face(vk::FrontFace::CLOCKWISE)
        .line_width(1.0);
    let multisample = vk::PipelineMultisampleStateCreateInfo::default()
        .rasterization_samples(vk::SampleCountFlags::TYPE_1);
    let depth_stencil = vk::PipelineDepthStencilStateCreateInfo::default()
        .depth_test_enable(true)
        .depth_write_enable(true)
        .depth_compare_op(vk::CompareOp::LESS)
        .depth_bounds_test_enable(true)
        .stencil_test_enable(false)
        .min_depth_bounds(0.0)
        .max_depth_bounds(1.0);

    let blend = vk::PipelineColorBlendAttachmentState::default()
        .src_color_blend_factor(vk::BlendFactor::SRC_COLOR)
        .dst_color_blend_factor(vk::BlendFactor::SRC_COLOR)
        .color_blend_op(vk::BlendOp::ADD)
        .src_alpha_blend_factor(vk::BlendFactor::SRC_COLOR)
        .dst_alpha_blend_factor(vk::BlendFactor::SRC_COLOR)
        .alpha_blend_op(vk::BlendOp::ADD)
        .color_write_mask(
            vk::ColorComponentFlags::R
                | vk::ColorComponentFlags::G
                | vk::ColorComponentFlags::B
                | vk::ColorComponentFlags::A,
        );

    let blend_attachments = [blend];

    let color_blend = vk::PipelineColorBlendStateCreateInfo::default()
        .attachments(blend_attachments.as_ref())
        .logic_op_enable(false)
        .logic_op(vk::LogicOp::COPY)
        .blend_constants([1.0, 1.0, 1.0, 1.0]);

    let dynamic = vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&[
        vk::DynamicState::VIEWPORT_WITH_COUNT,
        vk::DynamicState::SCISSOR_WITH_COUNT,
    ]);

    let pipe_info = vk::GraphicsPipelineCreateInfo::default()
        .stages(&shader_stages)
        .vertex_input_state(&vert_input)
        .input_assembly_state(&input_assembly)
        .rasterization_state(&rasterization)
        .multisample_state(&multisample)
        .depth_stencil_state(&depth_stencil)
        .color_blend_state(&color_blend)
        .layout(*pipe_layout)
        .dynamic_state(&dynamic)
        .render_pass(*pass)
        .subpass(0);

    let pipe = unsafe {
        dev.create_graphics_pipelines(vk::PipelineCache::null(), &[pipe_info], None)
            .expect("Failed to create graphics pipeline")
            .remove(0)
    };
    println!("Created pipeline");
    pipe
}

// TODO dynamic state for resizing
fn setup_swapchain(
    pdev: &vk::PhysicalDevice,
    dev: &Device,
    surface: &vk::SurfaceKHR,
    surface_loader: &surface::Instance,
    swapchain_loader: &ash::khr::swapchain::Device,
    instance: &Instance,
    width: u32,
    height: u32,
) -> (vk::SwapchainKHR, vk::Extent2D) {
    let caps = unsafe {
        surface_loader
            .get_physical_device_surface_capabilities(*pdev, *surface)
            .expect("Failed to get surface capabilities")
    };

    let formats = unsafe {
        surface_loader
            .get_physical_device_surface_formats(*pdev, *surface)
            .expect("Failed to get surface formats")
    };

    let format = (formats)
        .iter()
        .filter(|f| {
            f.format == vk::Format::B8G8R8A8_UNORM
                && f.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR
        })
        .next()
        .expect("No suitable format found");

    // When the window is resized fast, then the caps goes out of sync of the target extents
    // Thus we are using the passed-in (target) extents instead of the cap extents.
    // assert_eq!(caps.current_extent.width, width);
    // assert_eq!(caps.current_extent.height, height);

    let swap_create = vk::SwapchainCreateInfoKHR::default()
        .surface(*surface)
        .min_image_count(caps.min_image_count)
        .image_format(format.format)
        .image_color_space(format.color_space)
        .image_extent(vk::Extent2D {
            width: cmp::min(cmp::max(caps.current_extent.width, width), MAX_WIDTH),
            height: cmp::min(cmp::max(caps.current_extent.height, height), MAX_HEIGHT),
        })
        .image_array_layers(1)
        .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT)
        .image_sharing_mode(vk::SharingMode::EXCLUSIVE)
        .pre_transform(vk::SurfaceTransformFlagsKHR::IDENTITY)
        .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE)
        .present_mode(vk::PresentModeKHR::MAILBOX)
        .clipped(true);

    let swapchain = unsafe {
        swapchain_loader
            .create_swapchain(&swap_create, None)
            .expect("Failed to create swapchain")
    };
    println!("Created swapchain");
    (swapchain, caps.current_extent)
}

fn setup_desc_pool(dev: &Device) -> vk::DescriptorPool {
    let pool_size_storage = vk::DescriptorPoolSize::default()
        .ty(vk::DescriptorType::STORAGE_BUFFER)
        .descriptor_count(1);
    let pool_size_image = vk::DescriptorPoolSize::default()
        .ty(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
        .descriptor_count(1);
    let pool_sizes = [pool_size_storage, pool_size_image];

    let pool_info = vk::DescriptorPoolCreateInfo::default()
        .max_sets(1)
        .pool_sizes(&pool_sizes);
    let pool = unsafe {
        dev.create_descriptor_pool(&pool_info, None)
            .expect("Failed to create descriptor pool")
    };
    pool
}

fn make_swap_images(
    dev: &Device,
    swapchain: &vk::SwapchainKHR,
    swapchain_loader: &swapchain::Device,
) -> Vec<vk::Image> {
    unsafe {
        let images = swapchain_loader
            .get_swapchain_images(*swapchain)
            .expect("Failed to get swapchain images");
        println!("Fetched swapchain images");
        images
    }
}

fn make_swap_views(
    dev: &Device,
    swap_images: &Vec<vk::Image>,
    format: vk::Format,
) -> Vec<vk::ImageView> {
    let mut views = Vec::new();
    for img in swap_images.iter() {
        let view_info = vk::ImageViewCreateInfo::default()
            .image(*img)
            .view_type(vk::ImageViewType::TYPE_2D)
            .format(format)
            .components(vk::ComponentMapping::default())
            .subresource_range(
                vk::ImageSubresourceRange::default()
                    .aspect_mask(vk::ImageAspectFlags::COLOR)
                    .base_mip_level(0)
                    .level_count(1)
                    .base_array_layer(0)
                    .layer_count(1),
            );
        let view = unsafe {
            dev.create_image_view(&view_info, None)
                .expect("Failed to create image view")
        };
        views.push(view);
    }
    views
}

fn make_depth_view(dev: &Device, depth_img: &vk::Image, format: vk::Format) -> vk::ImageView {
    let view_info = vk::ImageViewCreateInfo::default()
        .image(*depth_img)
        .view_type(vk::ImageViewType::TYPE_2D)
        .format(format)
        .components(vk::ComponentMapping::default())
        .subresource_range(
            vk::ImageSubresourceRange::default()
                .aspect_mask(vk::ImageAspectFlags::DEPTH)
                .base_mip_level(0)
                .level_count(1)
                .base_array_layer(0)
                .layer_count(1),
        );
    let view = unsafe {
        dev.create_image_view(&view_info, None)
            .expect("Failed to create image view")
    };
    view
}

fn make_framebufs(
    dev: &Device,
    swap_views: &Vec<vk::ImageView>,
    depth_view: &vk::ImageView,
    pass: &vk::RenderPass,
    width: u32,
    height: u32,
) -> Vec<vk::Framebuffer> {
    let mut framebufs = Vec::new();
    for view in swap_views.iter() {
        let attachments = [*view, *depth_view];
        let framebuf_info = vk::FramebufferCreateInfo::default()
            .render_pass(*pass)
            .attachments(attachments.as_ref())
            .width(width)
            .height(height)
            .layers(1);
        let framebuf = unsafe {
            dev.create_framebuffer(&framebuf_info, None)
                .expect("Failed to create framebuffer")
        };
        framebufs.push(framebuf);
    }
    framebufs
}

fn make_desc_sets(dev: &Device, desc_pool: &vk::DescriptorPool) -> Vec<vk::DescriptorSet> {
    let layout = setup_desc_layout(&dev);
    let layouts = [layout];
    let alloc_info = vk::DescriptorSetAllocateInfo::default()
        .descriptor_pool(*desc_pool)
        .set_layouts(&layouts);
    let sets = unsafe {
        dev.allocate_descriptor_sets(&alloc_info)
            .expect("Failed to allocate descriptor sets")
    };
    sets
}

fn write_desc_sets(
    dev: &Device,
    desc_sets: &Vec<vk::DescriptorSet>,
    mesh_buf: &vk::Buffer,
    image_view: vk::ImageView,
    skybox_sampler: vk::Sampler
) {
    let buf_info = vk::DescriptorBufferInfo::default()
        .buffer(*mesh_buf)
        .offset(0)
        .range(vk::WHOLE_SIZE);
    let buf_infos = [buf_info];
    let buf_desc = vk::WriteDescriptorSet::default()
        .dst_set(desc_sets[0])
        .dst_binding(0)
        .dst_array_element(0)
        .descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
        .buffer_info(&buf_infos);

    let img_info = vk::DescriptorImageInfo::default()
        .image_layout(vk::ImageLayout::GENERAL)
        .image_view(image_view)
        .sampler(skybox_sampler);
    let img_infos = &[img_info];
    let img_desc = vk::WriteDescriptorSet::default()
        .dst_set(desc_sets[0])
        .dst_binding(1)
        .dst_array_element(0)
        .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
        .image_info(img_infos);

    unsafe {
        dev.update_descriptor_sets(&[buf_desc, img_desc], &[]);
    }
}

fn make_sem(dev: &Device) -> vk::Semaphore {
    let sem_info = vk::SemaphoreCreateInfo::default();
    let sem = unsafe {
        dev.create_semaphore(&sem_info, None)
            .expect("Failed to create semaphore")
    };
    sem
}

fn make_fence(dev: &Device) -> vk::Fence {
    let fence_info = vk::FenceCreateInfo::default().flags(vk::FenceCreateFlags::SIGNALED);
    let fence = unsafe {
        dev.create_fence(&fence_info, None)
            .expect("Failed to create fence")
    };
    fence
}

fn wait_for_fence(dev: &Device, fence: &vk::Fence) {
    unsafe {
        let fence = [*fence];
        dev.wait_for_fences(&fence, true, u64::MAX)
            .expect("Failed to wait for fence");
    }
}

fn reset_fence(dev: &Device, fence: &vk::Fence) {
    unsafe {
        let fence = [*fence];
        dev.reset_fences(&fence).expect("Failed to reset fence");
    }
}

fn ack_next_img(
    dev: &Device,
    swapchain_loader: &swapchain::Device,
    swapchain: &vk::SwapchainKHR,
    sem_avail: &vk::Semaphore,
    fence: &vk::Fence,
) -> u32 {
    unsafe {
        let (img_inx, _) = swapchain_loader
            .acquire_next_image(*swapchain, u64::MAX, *sem_avail, vk::Fence::null())
            .expect("Failed to acquire next image");
        img_inx
    }
}

fn record_commands(
    dev: &Device,
    cmd_buf: &vk::CommandBuffer,
    framebuf: &vk::Framebuffer,
    pass: &vk::RenderPass,
    layout: &vk::PipelineLayout,
    width: u32,
    height: u32,
    pipe: &vk::Pipeline,
    desc_sets: &Vec<vk::DescriptorSet>,
    camera: &Camera,
    skybox_buffer: &vk::Buffer,
    skybox_image: &vk::Image,
) {
    let clear_vals = [
        vk::ClearValue {
            color: vk::ClearColorValue {
                float32: [0.0, 0.0, 0.0, 1.0],
            },
        },
        vk::ClearValue {
            depth_stencil: vk::ClearDepthStencilValue {
                depth: 1.0,
                stencil: 0,
            },
        },
    ];

    let render_pass_info = vk::RenderPassBeginInfo::default()
        .render_pass(*pass)
        .framebuffer(*framebuf)
        .render_area(
            vk::Rect2D::default()
                .offset(vk::Offset2D::default())
                .extent(vk::Extent2D { width, height }),
        )
        .clear_values(&clear_vals);

    unsafe {
        let regions = (0..6)
        .map(|i| {
            vk::BufferImageCopy::default()
                .buffer_offset(3 * 2048 * 2048 * i)
                .buffer_row_length(0)
                .buffer_image_height(0)
                .image_offset(vk::Offset3D::default())
                .image_extent(vk::Extent3D::default().width(2048).height(2048).depth(1))
                .image_subresource(
                    vk::ImageSubresourceLayers::default()
                        .aspect_mask(vk::ImageAspectFlags::COLOR)
                        .mip_level(0)
                        .layer_count(1)
                        .base_array_layer(i as u32),
                )
        })
        .collect::<Vec<_>>();
        dev.cmd_copy_buffer_to_image(
            *cmd_buf,
            *skybox_buffer,
            *skybox_image,
            vk::ImageLayout::GENERAL,
            &regions,
        );
        dev.cmd_begin_render_pass(*cmd_buf, &render_pass_info, vk::SubpassContents::INLINE);
        dev.cmd_bind_pipeline(*cmd_buf, vk::PipelineBindPoint::GRAPHICS, *pipe);
        dev.cmd_bind_descriptor_sets(
            *cmd_buf,
            vk::PipelineBindPoint::GRAPHICS,
            *layout,
            0,
            desc_sets.as_ref(),
            &[],
        );
        dev.cmd_set_viewport_with_count(
            *cmd_buf,
            &[vk::Viewport::default()
                .width(width as f32)
                .height(height as f32)],
        );
        dev.cmd_set_scissor_with_count(
            *cmd_buf,
            &[vk::Rect2D::default().extent(vk::Extent2D { width, height })],
        );
        let cam_data: Vec<u8> = (Vec::<f32>::from([
            camera.origin.x,
            camera.origin.y,
            camera.origin.z,
            0.0,
            camera.rotation.x,
            camera.rotation.y,
            camera.rotation.z,
        ]))
        .iter()
        .map(|f| f.to_ne_bytes())
        .collect::<Vec<_>>()
        .into_flattened();

        let screen_res: Vec<u8> = (Vec::<u32>::from([width, height]))
            .iter()
            .map(|f| f.to_ne_bytes())
            .collect::<Vec<_>>()
            .into_flattened();
        let base_color: Vec<u8> = (Vec::<f32>::from([0.0, 1.0, 1.0, 1.0]))
            .iter()
            .map(|f| f.to_ne_bytes())
            .collect::<Vec<_>>()
            .into_flattened();

        dev.cmd_push_constants(
            *cmd_buf,
            *layout,
            vk::ShaderStageFlags::VERTEX,
            0,
            cam_data.as_ref(),
        );
        dev.cmd_push_constants(
            *cmd_buf,
            *layout,
            vk::ShaderStageFlags::VERTEX,
            32,
            screen_res.as_ref(),
        );
        dev.cmd_push_constants(
            *cmd_buf,
            *layout,
            vk::ShaderStageFlags::FRAGMENT,
            48,
            base_color.as_ref(),
        );



        dev.cmd_draw(*cmd_buf, 36, 1, 0, 0);
        dev.cmd_end_render_pass(*cmd_buf);
    }
}

fn start_cmd_buf(dev: &Device, cmd_buf: &vk::CommandBuffer) {
    unsafe {
        dev.reset_command_buffer(*cmd_buf, vk::CommandBufferResetFlags::empty())
            .expect("Failed to reset command buffer");
        let begin_info =
            vk::CommandBufferBeginInfo::default().flags(vk::CommandBufferUsageFlags::empty());
        dev.begin_command_buffer(*cmd_buf, &begin_info)
            .expect("Failed to begin command buffer");
    }
}

fn end_cmd_buf(dev: &Device, cmd_buf: &vk::CommandBuffer) {
    unsafe {
        dev.end_command_buffer(*cmd_buf)
            .expect("Failed to end command buffer");
    }
}

fn submit_queue(
    dev: &Device,
    queue: &vk::Queue,
    cmd_buf: &vk::CommandBuffer,
    sem_avail: &vk::Semaphore,
    sem_finish: &vk::Semaphore,
    fence: &vk::Fence,
) {
    let cmd_bufs = [*cmd_buf];
    let wait_sems = [*sem_avail];
    let signal_sems = [*sem_finish];
    let submit_info = vk::SubmitInfo::default()
        .command_buffers(&cmd_bufs)
        .wait_semaphores(&wait_sems)
        .wait_dst_stage_mask(&[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT])
        .signal_semaphores(&signal_sems);
    unsafe {
        dev.queue_submit(*queue, &[submit_info], *fence)
            .expect("Failed to submit queue");
    }
}

fn queue_present(
    swapchain_loader: &swapchain::Device,
    queue: &vk::Queue,
    swapchain: &vk::SwapchainKHR,
    img_inx: u32,
    sem_finish: &vk::Semaphore,
) {
    let swapchains = [*swapchain];
    let img_inxs = [img_inx];
    let wait_sems = [*sem_finish];
    let present_info = vk::PresentInfoKHR::default()
        .swapchains(&swapchains)
        .image_indices(&img_inxs)
        .wait_semaphores(&wait_sems);
    unsafe {
        swapchain_loader
            .queue_present(*queue, &present_info)
            .expect("Failed to present queue");
    }
}

#[derive(Debug)]
struct Vector3 {
    x: f32,
    y: f32,
    z: f32,
}

#[derive(Debug)]
struct Camera {
    origin: Vector3,
    rotation: Vector3,
}

fn handle_cont_key(keycode: Keycode, camera: &mut Camera, mouse: &MouseUtil) {
    match keycode {
        Keycode::W => {
            camera.origin.x -= -camera.rotation.y.sin() * MOV_STEP;
            camera.origin.z -= camera.rotation.y.cos() * MOV_STEP;
        }
        Keycode::S => {
            camera.origin.x += -camera.rotation.y.sin() * MOV_STEP;
            camera.origin.z += camera.rotation.y.cos() * MOV_STEP;
        }
        Keycode::A => {
            camera.origin.x += camera.rotation.y.cos() * MOV_STEP;
            camera.origin.z += camera.rotation.y.sin() * MOV_STEP;
        }
        Keycode::D => {
            camera.origin.z -= camera.rotation.y.sin() * MOV_STEP;
            camera.origin.x -= camera.rotation.y.cos() * MOV_STEP;
        }
        Keycode::Space => {
            camera.origin.y += MOV_STEP;
        }
        Keycode::LShift => {
            camera.origin.y -= MOV_STEP;
        }
        _ => {}
    }
}

fn decode_rif(raw: &[u8]) -> &[u8] {
    &raw[17..]
}

fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();
    let mouse = sdl_context.mouse();

    let mut width = 1024;
    let mut height = 768;

    let mut window = video_subsystem
        .window(APP_NAME, width, height)
        .vulkan()
        .resizable()
        .build()
        .unwrap();

    // We start in relative move
    mouse.set_relative_mouse_mode(true);

    let entry = Entry::linked();

    let instance = create_instance(&window, &entry);

    let surface = make_surface(&entry, &instance, &window);

    let pdev = find_pdev_dgpu(&instance);

    let rcs_queue_inx = find_rcs_queue_inx(&instance, &pdev).expect("No RCS queue found");

    let dev = make_dev(
        &instance,
        &pdev,
        rcs_queue_inx,
        vec![c"VK_KHR_swapchain", c"VK_EXT_extended_dynamic_state3"],
    );

    let queue = unsafe { dev.get_device_queue(rcs_queue_inx, 0) };

    let cmd_pool = make_command_pool(&dev, rcs_queue_inx);

    let cmd_buf = alloc_cmd_buf(&dev, cmd_pool);

    let host_visible_inx =
        find_host_visible(&instance, &pdev).expect("No host visible memory found");

    let host_invisible_inx =
        find_host_invisible(&instance, &pdev).expect("No host invisible memory found");

    let mesh_mem = mem_alloc(&dev, host_visible_inx, MESH_SIZE);
    // Max viewport size is 3440x1440
    let depth_mem = mem_alloc(
        &dev,
        host_invisible_inx,
        (MAX_WIDTH * MAX_HEIGHT * 8) as u64,
    );

    let mut depth_img = make_depth_img(
        &dev,
        host_invisible_inx,
        width,
        height,
        depth_mem,
        rcs_queue_inx,
    );

    let mesh_buf = alloc_buf(
        &dev,
        &mesh_mem,
        MESH_SIZE,
        vk::BufferUsageFlags::TRANSFER_SRC | vk::BufferUsageFlags::STORAGE_BUFFER,
    );

    let mesh_mem = buf_to_ptr(&dev, &mesh_mem, &mesh_buf);
    unsafe {
        std::ptr::copy(POSITIONS.as_ptr(), mesh_mem as *mut f32, POSITIONS.len());
    }
    
    let (skybox_image, skybox_image_view, skybox_sampler) = {
        let qf_idxs = [rcs_queue_inx];
        let create_info = vk::ImageCreateInfo::default()
            .flags(ImageCreateFlags::CUBE_COMPATIBLE)
            .image_type(vk::ImageType::TYPE_2D)
            .format(vk::Format::R8G8B8_UNORM)
            .extent(vk::Extent3D::default().width(2048).height(2048).depth(1))
            .mip_levels(1)
            .array_layers(6)
            .samples(vk::SampleCountFlags::TYPE_1)
            .tiling(vk::ImageTiling::OPTIMAL)
            .usage(vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::SAMPLED)
            .sharing_mode(vk::SharingMode::EXCLUSIVE)
            .initial_layout(vk::ImageLayout::UNDEFINED)
            .queue_family_indices(&qf_idxs);
        let image =
            unsafe { dev.create_image(&create_info, None) }.expect("Failed to create image");
        
        let skybox_mem_size: vk::MemoryRequirements = unsafe {dev.get_image_memory_requirements(image)};
        let skybox_dev_mem = mem_alloc(&dev, host_invisible_inx, skybox_mem_size.size);
        unsafe {
            dev.bind_image_memory(image, skybox_dev_mem, 0).expect("Failed to bind image memory");
        }
        let create_info = vk::ImageViewCreateInfo::default()
            .image(image)
            .view_type(vk::ImageViewType::CUBE)
            .format(vk::Format::R8G8B8_UNORM)
            .components(vk::ComponentMapping::default())
            .subresource_range(
                vk::ImageSubresourceRange::default()
                    .aspect_mask(vk::ImageAspectFlags::COLOR)
                    .base_mip_level(0)
                    .level_count(1)
                    .base_array_layer(0)
                    .layer_count(6),
            );
        let image_view = unsafe { dev.create_image_view(&create_info, None) }
            .expect("Failed to create image view");
        let create_info = vk::SamplerCreateInfo::default()
            .mag_filter(vk::Filter::LINEAR)
            .min_filter(vk::Filter::LINEAR)
            .mipmap_mode(vk::SamplerMipmapMode::LINEAR)
            .address_mode_u(vk::SamplerAddressMode::CLAMP_TO_EDGE)
            .address_mode_v(vk::SamplerAddressMode::CLAMP_TO_EDGE)
            .address_mode_w(vk::SamplerAddressMode::CLAMP_TO_EDGE)
            .max_anisotropy(1.0)
            .max_lod(1.0)
            .border_color(vk::BorderColor::FLOAT_OPAQUE_WHITE);
        let sampler =
            unsafe { dev.create_sampler(&create_info, None) }.expect("Failed to create sampler");


        (image, image_view, sampler)
    };

    let skybox_mem = mem_alloc(&dev, host_visible_inx, 3 * 2048 * 2048 * 6);
    let skybox_buf = alloc_buf(
        &dev,
        &skybox_mem,
        3 * 2048 * 2048 * 6,
        vk::BufferUsageFlags::TRANSFER_SRC,
    );
    let skybox_ptr = buf_to_ptr(&dev, &skybox_mem, &skybox_buf);
    let img = std::fs::read("resources/skybox.rif").unwrap();
    let data = decode_rif(&img);
    unsafe {
        std::ptr::copy(data.as_ptr(), skybox_ptr as *mut u8, data.len());
    }

    let vert_shader_bin = std::fs::read("shaders/vert.spv").unwrap();
    let frag_shader_bin = std::fs::read("shaders/frag.spv").unwrap();

    let vert_shader = unsafe {
        dev.create_shader_module(
            &vk::ShaderModuleCreateInfo {
                code_size: vert_shader_bin.len(),
                p_code: vert_shader_bin.as_ptr() as *const u32,
                ..Default::default()
            },
            None,
        )
        .expect("Failed to create vertex shader module")
    };

    let frag_shader = unsafe {
        dev.create_shader_module(
            &vk::ShaderModuleCreateInfo {
                code_size: frag_shader_bin.len(),
                p_code: frag_shader_bin.as_ptr() as *const u32,
                ..Default::default()
            },
            None,
        )
        .expect("Failed to create fragment shader module")
    };

    let pass = setup_render_pass(&dev);

    let pipe_layout = setup_pipe_layout(&dev);
    let pipe = setup_pipeline(&dev, &vert_shader, &frag_shader, &pass, &pipe_layout);

    let surface_loader = surface::Instance::new(&entry, &instance);
    let swapchain_loader = swapchain::Device::new(&instance, &dev);

    let (mut swapchain, mut extents) = setup_swapchain(
        &pdev,
        &dev,
        &surface,
        &surface_loader,
        &swapchain_loader,
        &instance,
        width,
        height,
    );

    let desc_pool = setup_desc_pool(&dev);

    let mut swap_images = make_swap_images(&dev, &swapchain, &swapchain_loader);

    let mut swap_views = make_swap_views(&dev, &swap_images, vk::Format::B8G8R8A8_UNORM);

    let mut depth_view = make_depth_view(&dev, &depth_img, vk::Format::D32_SFLOAT);

    let mut framebufs = make_framebufs(&dev, &swap_views, &depth_view, &pass, width, height);

    let desc_sets = make_desc_sets(&dev, &desc_pool);

    write_desc_sets(&dev, &desc_sets, &mesh_buf, skybox_image_view, skybox_sampler);

    let sem_avail = make_sem(&dev);
    let sem_finish = make_sem(&dev);
    let fence_flight = make_fence(&dev);

    let mut event_pump = sdl_context.event_pump().expect("Failed to get event pump");
    let mut camera = Camera {
        origin: Vector3 {
            x: 0.0,
            y: 0.0,
            z: 0.0,
        },
        rotation: Vector3 {
            x: 0.0,
            y: 0.0,
            z: 0.0,
        },
    };

    let mut running = true;
    while running {
        let now = Instant::now();
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit { .. } => running = false,
                Event::Window {
                    win_event: WindowEvent::Resized(w, h),
                    ..
                } => {
                    width = w as u32;
                    height = h as u32;
                    unsafe {
                        dev.device_wait_idle()
                            .expect("Failed to wait for device idle");
                        swapchain_loader.destroy_swapchain(swapchain, None);
                    }
                    (swapchain, _) = setup_swapchain(
                        &pdev,
                        &dev,
                        &surface,
                        &surface_loader,
                        &swapchain_loader,
                        &instance,
                        width,
                        height,
                    );
                    swap_images = make_swap_images(&dev, &swapchain, &swapchain_loader);
                    swap_views = make_swap_views(&dev, &swap_images, vk::Format::B8G8R8A8_UNORM);
                    depth_img = make_depth_img(
                        &dev,
                        host_invisible_inx,
                        width,
                        height,
                        depth_mem,
                        rcs_queue_inx,
                    );
                    depth_view = make_depth_view(&dev, &depth_img, vk::Format::D32_SFLOAT);
                    framebufs =
                        make_framebufs(&dev, &swap_views, &depth_view, &pass, width, height);
                }
                // We do this key here and not in `handle_cont_key()` because we don't want key
                // repeat, just a single press
                Event::KeyDown {
                    keycode: Some(Keycode::ESCAPE),
                    repeat: false,
                    ..
                } => {
                    mouse.set_relative_mouse_mode(!mouse.relative_mouse_mode());
                }
                Event::MouseMotion {
                    window_id,
                    xrel,
                    yrel,
                    ..
                } if mouse.relative_mouse_mode() => {
                    // We only wanna do movement if the mouse is in relative mode (ie. its pinned to
                    // the center of the window and hidden)
                    camera.rotation.y -= xrel as f32 * ROT_MOUSE_SCALE;
                    camera.rotation.x -= yrel as f32 * ROT_MOUSE_SCALE;
                    mouse.warp_mouse_in_window(&window, width as i32 / 2, height as i32 / 2);
                }
                _ => {}
            }
        }

        event_pump
            .keyboard_state()
            .pressed_scancodes()
            .filter_map(Keycode::from_scancode)
            .for_each(|k| handle_cont_key(k, &mut camera, &mouse));

        wait_for_fence(&dev, &fence_flight);
        reset_fence(&dev, &fence_flight);

        let img_inx = ack_next_img(
            &dev,
            &swapchain_loader,
            &swapchain,
            &sem_avail,
            &fence_flight,
        );

        start_cmd_buf(&dev, &cmd_buf);

        record_commands(
            &dev,
            &cmd_buf,
            &framebufs[img_inx as usize],
            &pass,
            &pipe_layout,
            width,
            height,
            &pipe,
            &desc_sets,
            &camera,
            &skybox_buf,
            &skybox_image,
        );
        end_cmd_buf(&dev, &cmd_buf);

        submit_queue(
            &dev,
            &queue,
            &cmd_buf,
            &sem_avail,
            &sem_finish,
            &fence_flight,
        );

        queue_present(&swapchain_loader, &queue, &swapchain, img_inx, &sem_finish);

        let elapsed = now.elapsed();
        let delay = (1_000_000u128 / 60).saturating_sub(elapsed.as_micros());
        thread::sleep(Duration::from_micros(delay as u64));
    }
}
