diff --git a/crates/renderer/src/buffers.rs b/crates/renderer/src/buffers.rs
new file mode 100644
index 0000000..0e3e977
--- /dev/null
+++ b/crates/renderer/src/buffers.rs
@@ -0,0 +1,88 @@
+use std::{ops::Deref, sync::Arc};
+
+use ash::{prelude::VkResult, vk};
+use vk_mem::Alloc;
+
+use crate::Device;
+
+pub struct Buffer {
+    device: Device,
+    buffer: vk::Buffer,
+    allocation: vk_mem::Allocation,
+    usage: vk::BufferUsageFlags,
+    size: u64,
+}
+
+impl Drop for Buffer {
+    fn drop(&mut self) {
+        unsafe {
+            self.device
+                .alloc()
+                .destroy_buffer(self.buffer, &mut self.allocation);
+        }
+    }
+}
+
+impl Buffer {
+    pub fn new(
+        device: Device,
+        size: usize,
+        usage: vk::BufferUsageFlags,
+        queue_families: &[u32],
+        memory_usage: vk_mem::MemoryUsage,
+        alloc_flags: vk_mem::AllocationCreateFlags,
+    ) -> VkResult<Arc<Self>> {
+        let sharing_mode = if queue_families.len() > 1 {
+            vk::SharingMode::EXCLUSIVE
+        } else {
+            vk::SharingMode::CONCURRENT
+        };
+
+        let (buffer, allocation) = unsafe {
+            device.alloc().create_buffer(
+                &vk::BufferCreateInfo::default()
+                    .usage(usage)
+                    .queue_family_indices(queue_families)
+                    .sharing_mode(sharing_mode),
+                &vk_mem::AllocationCreateInfo {
+                    flags: alloc_flags,
+                    usage: memory_usage,
+                    ..Default::default()
+                },
+            )?
+        };
+
+        let buffer = Self {
+            device,
+            buffer,
+            allocation,
+            usage,
+            size: size as u64,
+        };
+
+        Ok(Arc::new(buffer))
+    }
+
+    pub fn map(&mut self) -> VkResult<MappedBuffer<'_>> {
+        let bytes = unsafe {
+            let data = self.device.alloc().map_memory(&mut self.allocation)?;
+            let slice = core::slice::from_raw_parts(data, self.size as usize);
+
+            slice
+        };
+
+        Ok(MappedBuffer { bytes })
+    }
+}
+
+pub struct MappedBuffer<'a> {
+    bytes: &'a [u8],
+}
+
+impl Deref for MappedBuffer<'_> {
+    type Target = [u8];
+
+    fn deref(&self) -> &Self::Target {
+        self.bytes
+    }
+}
diff --git a/crates/renderer/src/commands.rs b/crates/renderer/src/commands.rs
index a828988..819a271 100644
--- a/crates/renderer/src/commands.rs
+++ b/crates/renderer/src/commands.rs
@@ -1,20 +1,28 @@
 use std::{future::Future, marker::PhantomData, sync::Arc};
 
-use crate::sync::{self, FenceFuture};
+use crate::{
+    buffers::Buffer,
+    images::{Image2D, QueueOwnership},
+    sync::{self, FenceFuture},
+    util::{FormatExt, MutexExt},
+};
 
 use super::{Device, Queue};
 use ash::{prelude::*, vk};
+use parking_lot::Mutex;
 
 pub struct SingleUseCommandPool {
     device: Device,
-    pool: vk::CommandPool,
+    pool: Mutex<vk::CommandPool>,
     queue: Queue,
 }
 
 impl Drop for SingleUseCommandPool {
     fn drop(&mut self) {
         unsafe {
-            self.device.dev().destroy_command_pool(self.pool, None);
+            self.pool.with_locked(|&pool| {
+                self.device.dev().destroy_command_pool(pool, None);
+            });
         }
     }
 }
@@ -30,7 +38,7 @@ impl SingleUseCommandPool {
 
         Ok(Arc::new(Self {
             device,
-            pool,
+            pool: Mutex::new(pool),
             queue,
         }))
     }
@@ -43,8 +51,9 @@ impl SingleUseCommandPool {
         &self.queue
     }
 
-    pub fn pool(&self) -> vk::CommandPool {
-        self.pool
+    /// get the underlying pool, bypassing the mutex
+    pub unsafe fn pool(&self) -> vk::CommandPool {
+        self.pool.data_ptr().read()
     }
 }
 
@@ -54,12 +63,14 @@ pub struct SingleUseCommand {
     buffer: vk::CommandBuffer,
 }
 
+impl !Sync for SingleUseCommand {}
+
 impl Drop for SingleUseCommand {
     fn drop(&mut self) {
         unsafe {
-            self.device
-                .dev()
-                .free_command_buffers(self.pool.pool(), &[self.buffer])
+            self.pool.pool.with_locked(|&pool| {
+                self.device.dev().free_command_buffers(pool, &[self.buffer])
+            })
         };
     }
 }
@@ -91,10 +102,89 @@ impl SingleUseCommand {
         })
     }
 
-    pub fn buffer(&self) -> vk::CommandBuffer {
+    /// Safety: commandbuffer must not be accessed from multiple threads at the same time
+    pub unsafe fn buffer(&self) -> vk::CommandBuffer {
         self.buffer
     }
 
+    //pub fn copy_buffer_to_image(&self, image: &Image2D, buffer: &Buffer, )
+
+    pub fn image_barrier(
+        &self,
+        image: vk::Image,
+        aspects: vk::ImageAspectFlags,
+        src_stage: vk::PipelineStageFlags2,
+        src_access: vk::AccessFlags2,
+        dst_stage: vk::PipelineStageFlags2,
+        dst_access: vk::AccessFlags2,
+        old_layout: vk::ImageLayout,
+        new_layout: vk::ImageLayout,
+        queue_ownership_op: Option<QueueOwnership>,
+    ) {
+        let (src_family, dst_family) = queue_ownership_op
+            .map(|t| (t.src, t.dst))
+            .unwrap_or((vk::QUEUE_FAMILY_IGNORED, vk::QUEUE_FAMILY_IGNORED));
+
+        let barrier = vk::ImageMemoryBarrier2::default()
+            .image(image)
+            .subresource_range(
+                vk::ImageSubresourceRange::default()
+                    .aspect_mask(aspects)
+                    .base_mip_level(0)
+                    .base_array_layer(0)
+                    .level_count(vk::REMAINING_MIP_LEVELS)
+                    .layer_count(vk::REMAINING_ARRAY_LAYERS),
+            )
+            .src_stage_mask(src_stage)
+            .src_access_mask(src_access)
+            .dst_stage_mask(dst_stage)
+            .dst_access_mask(dst_access)
+            .dst_queue_family_index(dst_family)
+            .src_queue_family_index(src_family)
+            .old_layout(old_layout)
+            .new_layout(new_layout);
+
+        unsafe {
+            self.device.dev().cmd_pipeline_barrier2(
+                self.buffer,
+                &vk::DependencyInfo::default()
+                    .dependency_flags(vk::DependencyFlags::BY_REGION)
+                    .image_memory_barriers(core::slice::from_ref(&barrier)),
+            );
+        }
+    }
+
+    pub fn clear_color_image(
+        &self,
+        image: vk::Image,
+        format: vk::Format,
+        layout: vk::ImageLayout,
+        color: crate::Rgba,
+        subresources: &[vk::ImageSubresourceRange],
+    ) {
+        let clear_colors = match format.get_component_kind() {
+            crate::util::FormatComponentKind::Float => vk::ClearColorValue {
+                float32: color.into_f32(),
+            },
+            crate::util::FormatComponentKind::UInt => vk::ClearColorValue {
+                uint32: color.into_u32(),
+            },
+            crate::util::FormatComponentKind::SInt => vk::ClearColorValue {
+                int32: color.into_i32(),
+            },
+        };
+
+        unsafe {
+            self.device.dev().cmd_clear_color_image(
+                self.buffer,
+                image,
+                layout,
+                &clear_colors,
+                subresources,
+            );
+        }
+    }
+
     pub fn submit_fence(
         &self,
         wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
diff --git a/crates/renderer/src/images.rs b/crates/renderer/src/images.rs
index 1182ae1..d50c37d 100644
--- a/crates/renderer/src/images.rs
+++ b/crates/renderer/src/images.rs
@@ -1,5 +1,7 @@
 use std::sync::Arc;
 
+use crate::buffers::Buffer;
+
 use super::{Device, Queue};
 use ash::{prelude::*, vk};
 use vk_mem::Alloc;
@@ -70,6 +72,16 @@ impl Image2D {
         }))
     }
 
+    pub fn format(&self) -> vk::Format {
+        self.format
+    }
+
+    pub fn copy_from_buffer(&self, buffer: &Buffer) {
+        unsafe {
+            // self.device.dev().cmd_copy_buffer_to_image(command_buffer, src_buffer, dst_image, dst_image_layout, regions);
+        }
+    }
+
     pub fn view(
         self: &Arc<Self>,
         device: &Device,
@@ -121,8 +133,8 @@ impl Drop for ImageView2D {
 impl ImageView2D {}
 
 pub struct QueueOwnership {
-    src: u32,
-    dst: u32,
+    pub src: u32,
+    pub dst: u32,
 }
 
 pub fn image_barrier<'a>(
@@ -159,3 +171,12 @@ pub fn image_barrier<'a>(
         .old_layout(old_layout)
         .new_layout(new_layout)
 }
+
+pub const SUBRESOURCERANGE_COLOR_ALL: vk::ImageSubresourceRange =
+    vk::ImageSubresourceRange {
+        aspect_mask: vk::ImageAspectFlags::COLOR,
+        base_mip_level: 0,
+        level_count: vk::REMAINING_MIP_LEVELS,
+        base_array_layer: 0,
+        layer_count: vk::REMAINING_ARRAY_LAYERS,
+    };
diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs
index 1cf1afc..186171b 100644
--- a/crates/renderer/src/lib.rs
+++ b/crates/renderer/src/lib.rs
@@ -33,11 +33,14 @@ use raw_window_handle::{DisplayHandle, RawDisplayHandle};
 use tinyvec::{array_vec, ArrayVec};
 use tracing::info;
 
+mod buffers;
 mod commands;
 mod images;
 mod render_graph;
-
 mod sync;
+mod util;
+
+use render_graph::Rgba;
 
 #[derive(Debug, thiserror::Error)]
 pub enum Error {
@@ -720,6 +723,7 @@ pub struct SwapchainFrame {
     pub swapchain: Arc<Swapchain>,
     pub index: u32,
     pub image: vk::Image,
+    pub format: vk::Format,
     pub view: vk::ImageView,
     pub acquire: vk::Semaphore,
     pub release: vk::Semaphore,
@@ -1042,6 +1046,7 @@ impl Swapchain {
                 SwapchainFrame {
                     index: idx as u32,
                     swapchain: self.clone(),
+                    format: self.format,
                     image,
                     view,
                     acquire,
@@ -2043,7 +2048,6 @@ impl<W> Renderer<W> {
 
         if let Some(ctx) = self.window_contexts.get(window) {
             let cmd = pool.alloc()?;
-            let buffer = cmd.buffer();
 
             let (frame, suboptimal) = smol::block_on(
                 ctx.current_swapchain.read().clone().acquire_image(),
@@ -2074,6 +2078,7 @@ impl<W> Renderer<W> {
                 ctx.surface.surface.as_raw(),
             )
             .gen::<[f32; 3]>();
+            let clear_color = Rgba([r, g, b, 1.0]);
             let clear_values = vk::ClearColorValue {
                 float32: [r, g, b, 1.0],
             };
@@ -2095,21 +2100,25 @@ impl<W> Renderer<W> {
                     .dependency_flags(vk::DependencyFlags::BY_REGION)
                     .image_memory_barriers(&barriers);
 
-                dev.dev().cmd_pipeline_barrier2(buffer, &dependency_info);
-                dev.dev().cmd_clear_color_image(
-                    buffer,
+                cmd.image_barrier(
                     frame.image,
+                    vk::ImageAspectFlags::COLOR,
+                    vk::PipelineStageFlags2::TRANSFER,
+                    vk::AccessFlags2::empty(),
+                    vk::PipelineStageFlags2::TRANSFER,
+                    vk::AccessFlags2::TRANSFER_WRITE,
+                    vk::ImageLayout::UNDEFINED,
                     vk::ImageLayout::TRANSFER_DST_OPTIMAL,
-                    &clear_values,
-                    &[vk::ImageSubresourceRange::default()
-                        .aspect_mask(vk::ImageAspectFlags::COLOR)
-                        .base_mip_level(0)
-                        .base_array_layer(0)
-                        .level_count(vk::REMAINING_MIP_LEVELS)
-                        .layer_count(vk::REMAINING_ARRAY_LAYERS)],
+                    None,
                 );
-
-                let barriers = [images::image_barrier(
+                cmd.clear_color_image(
+                    frame.image,
+                    frame.format,
+                    vk::ImageLayout::TRANSFER_DST_OPTIMAL,
+                    clear_color,
+                    &[images::SUBRESOURCERANGE_COLOR_ALL],
+                );
+                cmd.image_barrier(
                     frame.image,
                     vk::ImageAspectFlags::COLOR,
                     vk::PipelineStageFlags2::TRANSFER,
@@ -2119,16 +2128,10 @@ impl<W> Renderer<W> {
                     vk::ImageLayout::TRANSFER_DST_OPTIMAL,
                     vk::ImageLayout::PRESENT_SRC_KHR,
                     None,
-                )];
-
-                let dependency_info = vk::DependencyInfo::default()
-                    .dependency_flags(vk::DependencyFlags::BY_REGION)
-                    .image_memory_barriers(&barriers);
-
-                dev.dev().cmd_pipeline_barrier2(buffer, &dependency_info);
+                );
 
                 let future = cmd.submit_async(
-                    Some((frame.acquire, vk::PipelineStageFlags::ALL_COMMANDS)),
+                    Some((frame.acquire, vk::PipelineStageFlags::TRANSFER)),
                     Some(frame.release),
                     Arc::new(sync::Fence::create(dev.clone())?),
                 )?;
diff --git a/crates/renderer/src/render_graph.rs b/crates/renderer/src/render_graph.rs
index f48d7c8..b70db29 100644
--- a/crates/renderer/src/render_graph.rs
+++ b/crates/renderer/src/render_graph.rs
@@ -1,6 +1,44 @@
+use std::hash::Hash;
+
 use ash::vk;
 
-struct Rgba([f32; 4]);
+#[derive(Debug, Clone, Copy)]
+pub struct Rgba(pub [f32; 4]);
+
+impl std::hash::Hash for Rgba {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        fn hash_f32<H: std::hash::Hasher>(state: &mut H, f: f32) {
+            let classify = f.classify();
+            match classify {
+                std::num::FpCategory::Nan => (classify as u8).hash(state),
+                std::num::FpCategory::Infinite | std::num::FpCategory::Zero => {
+                    (classify as u8, f.signum() as i8).hash(state)
+                }
+                std::num::FpCategory::Subnormal
+                | std::num::FpCategory::Normal => f.to_bits().hash(state),
+            }
+        }
+        self.0.map(|f| hash_f32(state, f));
+    }
+}
+
+impl Rgba {
+    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
+        Self([r, g, b, a])
+    }
+    pub fn into_u32(&self) -> [u32; 4] {
+        self.0.map(|f| (f.clamp(0.0, 1.0) * 255.0) as u32)
+    }
+    pub fn into_f32(&self) -> [f32; 4] {
+        self.0
+    }
+    pub fn into_snorm(&self) -> [f32; 4] {
+        self.0.map(|f| (f - 0.5) * 2.0)
+    }
+    pub fn into_i32(&self) -> [i32; 4] {
+        self.0.map(|f| (f.clamp(0.0, 1.0) * 255.0) as i32 - 128)
+    }
+}
 
 enum LoadOp {
     Clear(Rgba),
diff --git a/crates/renderer/src/util.rs b/crates/renderer/src/util.rs
new file mode 100644
index 0000000..82400ef
--- /dev/null
+++ b/crates/renderer/src/util.rs
@@ -0,0 +1,241 @@
+use std::ops::Deref;
+
+use ash::vk;
+
+pub trait MutexExt<'a, T: 'a> {
+    type Guard: Deref<Target = T> + 'a;
+    fn lock(&'a self) -> Self::Guard;
+    fn with_locked<R, F: FnOnce(&T) -> R>(&'a self, f: F) -> R {
+        let lock = MutexExt::lock(self);
+        f(&*lock)
+    }
+}
+
+impl<'a, T: 'a> MutexExt<'a, T> for parking_lot::Mutex<T> {
+    type Guard = parking_lot::MutexGuard<'a, T>;
+    fn lock(&'a self) -> Self::Guard {
+        self.lock()
+    }
+}
+
+pub trait FormatExt {
+    fn get_component_kind(&self) -> FormatComponentKind;
+    fn is_f32(&self) -> bool;
+    fn is_u32(&self) -> bool;
+    fn is_i32(&self) -> bool;
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum FormatComponentKind {
+    Float,
+    UInt,
+    SInt,
+}
+
+fn format_to_primitive(format: vk::Format) -> FormatComponentKind {
+    use FormatComponentKind::*;
+    match format {
+        vk::Format::BC6H_SFLOAT_BLOCK => Float,
+        vk::Format::D32_SFLOAT => Float,
+        vk::Format::R16G16B16A16_SFLOAT => Float,
+        vk::Format::R16G16B16_SFLOAT => Float,
+        vk::Format::R16G16_SFLOAT => Float,
+        vk::Format::R16_SFLOAT => Float,
+        vk::Format::R32G32B32A32_SFLOAT => Float,
+        vk::Format::R32G32B32_SFLOAT => Float,
+        vk::Format::R32G32_SFLOAT => Float,
+        vk::Format::R32_SFLOAT => Float,
+        vk::Format::R64G64B64A64_SFLOAT => Float,
+        vk::Format::R64G64B64_SFLOAT => Float,
+        vk::Format::R64G64_SFLOAT => Float,
+        vk::Format::R64_SFLOAT => Float,
+        vk::Format::D32_SFLOAT_S8_UINT => Float,
+        vk::Format::A2B10G10R10_SINT_PACK32 => SInt,
+        vk::Format::A2R10G10B10_SINT_PACK32 => SInt,
+        vk::Format::A8B8G8R8_SINT_PACK32 => SInt,
+        vk::Format::B8G8R8A8_SINT => SInt,
+        vk::Format::B8G8R8_SINT => SInt,
+        vk::Format::R16G16B16A16_SINT => SInt,
+        vk::Format::R16G16B16_SINT => SInt,
+        vk::Format::R16G16_SINT => SInt,
+        vk::Format::R16_SINT => SInt,
+        vk::Format::R32G32B32A32_SINT => SInt,
+        vk::Format::R32G32B32_SINT => SInt,
+        vk::Format::R32G32_SINT => SInt,
+        vk::Format::R32_SINT => SInt,
+        vk::Format::R64G64B64A64_SINT => SInt,
+        vk::Format::R64G64B64_SINT => SInt,
+        vk::Format::R64G64_SINT => SInt,
+        vk::Format::R64_SINT => SInt,
+        vk::Format::R8G8B8A8_SINT => SInt,
+        vk::Format::R8G8B8_SINT => SInt,
+        vk::Format::R8G8_SINT => SInt,
+        vk::Format::R8_SINT => SInt,
+        vk::Format::A2B10G10R10_SNORM_PACK32 => Float,
+        vk::Format::A2R10G10B10_SNORM_PACK32 => Float,
+        vk::Format::A8B8G8R8_SNORM_PACK32 => Float,
+        vk::Format::B8G8R8A8_SNORM => Float,
+        vk::Format::B8G8R8_SNORM => Float,
+        vk::Format::BC4_SNORM_BLOCK => Float,
+        vk::Format::BC5_SNORM_BLOCK => Float,
+        vk::Format::EAC_R11G11_SNORM_BLOCK => Float,
+        vk::Format::EAC_R11_SNORM_BLOCK => Float,
+        vk::Format::R16G16B16A16_SNORM => Float,
+        vk::Format::R16G16B16_SNORM => Float,
+        vk::Format::R16G16_SNORM => Float,
+        vk::Format::R16_SNORM => Float,
+        vk::Format::R8G8B8A8_SNORM => Float,
+        vk::Format::R8G8B8_SNORM => Float,
+        vk::Format::R8G8_SNORM => Float,
+        vk::Format::R8_SNORM => Float,
+        vk::Format::A8B8G8R8_SRGB_PACK32 => Float,
+        vk::Format::ASTC_10X10_SRGB_BLOCK => Float,
+        vk::Format::ASTC_10X5_SRGB_BLOCK => Float,
+        vk::Format::ASTC_10X6_SRGB_BLOCK => Float,
+        vk::Format::ASTC_10X8_SRGB_BLOCK => Float,
+        vk::Format::ASTC_12X10_SRGB_BLOCK => Float,
+        vk::Format::ASTC_12X12_SRGB_BLOCK => Float,
+        vk::Format::ASTC_4X4_SRGB_BLOCK => Float,
+        vk::Format::ASTC_5X4_SRGB_BLOCK => Float,
+        vk::Format::ASTC_5X5_SRGB_BLOCK => Float,
+        vk::Format::ASTC_6X5_SRGB_BLOCK => Float,
+        vk::Format::ASTC_6X6_SRGB_BLOCK => Float,
+        vk::Format::ASTC_8X5_SRGB_BLOCK => Float,
+        vk::Format::ASTC_8X6_SRGB_BLOCK => Float,
+        vk::Format::ASTC_8X8_SRGB_BLOCK => Float,
+        vk::Format::B8G8R8A8_SRGB => Float,
+        vk::Format::B8G8R8_SRGB => Float,
+        vk::Format::BC1_RGBA_SRGB_BLOCK => Float,
+        vk::Format::BC1_RGB_SRGB_BLOCK => Float,
+        vk::Format::BC2_SRGB_BLOCK => Float,
+        vk::Format::BC3_SRGB_BLOCK => Float,
+        vk::Format::BC7_SRGB_BLOCK => Float,
+        vk::Format::ETC2_R8G8B8A1_SRGB_BLOCK => Float,
+        vk::Format::ETC2_R8G8B8A8_SRGB_BLOCK => Float,
+        vk::Format::ETC2_R8G8B8_SRGB_BLOCK => Float,
+        vk::Format::R8G8B8A8_SRGB => Float,
+        vk::Format::R8G8B8_SRGB => Float,
+        vk::Format::R8G8_SRGB => Float,
+        vk::Format::R8_SRGB => Float,
+        vk::Format::A2B10G10R10_SSCALED_PACK32 => Float,
+        vk::Format::A2R10G10B10_SSCALED_PACK32 => Float,
+        vk::Format::A8B8G8R8_SSCALED_PACK32 => Float,
+        vk::Format::B8G8R8A8_SSCALED => Float,
+        vk::Format::B8G8R8_SSCALED => Float,
+        vk::Format::R16G16B16A16_SSCALED => Float,
+        vk::Format::R16G16B16_SSCALED => Float,
+        vk::Format::R16G16_SSCALED => Float,
+        vk::Format::R16_SSCALED => Float,
+        vk::Format::R8G8B8A8_SSCALED => Float,
+        vk::Format::R8G8B8_SSCALED => Float,
+        vk::Format::R8G8_SSCALED => Float,
+        vk::Format::R8_SSCALED => Float,
+        vk::Format::B10G11R11_UFLOAT_PACK32 => Float,
+        vk::Format::BC6H_UFLOAT_BLOCK => Float,
+        vk::Format::E5B9G9R9_UFLOAT_PACK32 => Float,
+        vk::Format::A2B10G10R10_UINT_PACK32 => UInt,
+        vk::Format::A2R10G10B10_UINT_PACK32 => UInt,
+        vk::Format::A8B8G8R8_UINT_PACK32 => UInt,
+        vk::Format::B8G8R8A8_UINT => UInt,
+        vk::Format::B8G8R8_UINT => UInt,
+        vk::Format::R16G16B16A16_UINT => UInt,
+        vk::Format::R16G16B16_UINT => UInt,
+        vk::Format::R16G16_UINT => UInt,
+        vk::Format::R16_UINT => UInt,
+        vk::Format::R32G32B32A32_UINT => UInt,
+        vk::Format::R32G32B32_UINT => UInt,
+        vk::Format::R32G32_UINT => UInt,
+        vk::Format::R32_UINT => UInt,
+        vk::Format::R64G64B64A64_UINT => UInt,
+        vk::Format::R64G64B64_UINT => UInt,
+        vk::Format::R64G64_UINT => UInt,
+        vk::Format::R64_UINT => UInt,
+        vk::Format::R8G8B8A8_UINT => UInt,
+        vk::Format::R8G8B8_UINT => UInt,
+        vk::Format::R8G8_UINT => UInt,
+        vk::Format::R8_UINT => UInt,
+        vk::Format::S8_UINT => UInt,
+        vk::Format::A1R5G5B5_UNORM_PACK16 => Float,
+        vk::Format::A2B10G10R10_UNORM_PACK32 => Float,
+        vk::Format::A2R10G10B10_UNORM_PACK32 => Float,
+        vk::Format::A8B8G8R8_UNORM_PACK32 => Float,
+        vk::Format::ASTC_10X10_UNORM_BLOCK => Float,
+        vk::Format::ASTC_10X5_UNORM_BLOCK => Float,
+        vk::Format::ASTC_10X6_UNORM_BLOCK => Float,
+        vk::Format::ASTC_10X8_UNORM_BLOCK => Float,
+        vk::Format::ASTC_12X10_UNORM_BLOCK => Float,
+        vk::Format::ASTC_12X12_UNORM_BLOCK => Float,
+        vk::Format::ASTC_4X4_UNORM_BLOCK => Float,
+        vk::Format::ASTC_5X4_UNORM_BLOCK => Float,
+        vk::Format::ASTC_5X5_UNORM_BLOCK => Float,
+        vk::Format::ASTC_6X5_UNORM_BLOCK => Float,
+        vk::Format::ASTC_6X6_UNORM_BLOCK => Float,
+        vk::Format::ASTC_8X5_UNORM_BLOCK => Float,
+        vk::Format::ASTC_8X6_UNORM_BLOCK => Float,
+        vk::Format::ASTC_8X8_UNORM_BLOCK => Float,
+        vk::Format::B4G4R4A4_UNORM_PACK16 => Float,
+        vk::Format::B5G5R5A1_UNORM_PACK16 => Float,
+        vk::Format::B5G6R5_UNORM_PACK16 => Float,
+        vk::Format::B8G8R8A8_UNORM => Float,
+        vk::Format::B8G8R8_UNORM => Float,
+        vk::Format::BC1_RGBA_UNORM_BLOCK => Float,
+        vk::Format::BC1_RGB_UNORM_BLOCK => Float,
+        vk::Format::BC2_UNORM_BLOCK => Float,
+        vk::Format::BC3_UNORM_BLOCK => Float,
+        vk::Format::BC4_UNORM_BLOCK => Float,
+        vk::Format::BC5_UNORM_BLOCK => Float,
+        vk::Format::BC7_UNORM_BLOCK => Float,
+        vk::Format::D16_UNORM => Float,
+        vk::Format::D16_UNORM_S8_UINT => Float,
+        vk::Format::D24_UNORM_S8_UINT => Float,
+        vk::Format::EAC_R11G11_UNORM_BLOCK => Float,
+        vk::Format::EAC_R11_UNORM_BLOCK => Float,
+        vk::Format::ETC2_R8G8B8A1_UNORM_BLOCK => Float,
+        vk::Format::ETC2_R8G8B8A8_UNORM_BLOCK => Float,
+        vk::Format::ETC2_R8G8B8_UNORM_BLOCK => Float,
+        vk::Format::R16G16B16A16_UNORM => Float,
+        vk::Format::R16G16B16_UNORM => Float,
+        vk::Format::R16G16_UNORM => Float,
+        vk::Format::R16_UNORM => Float,
+        vk::Format::R4G4B4A4_UNORM_PACK16 => Float,
+        vk::Format::R4G4_UNORM_PACK8 => Float,
+        vk::Format::R5G5B5A1_UNORM_PACK16 => Float,
+        vk::Format::R5G6B5_UNORM_PACK16 => Float,
+        vk::Format::R8G8B8A8_UNORM => Float,
+        vk::Format::R8G8B8_UNORM => Float,
+        vk::Format::R8G8_UNORM => Float,
+        vk::Format::R8_UNORM => Float,
+        vk::Format::X8_D24_UNORM_PACK32 => Float,
+        vk::Format::A2B10G10R10_USCALED_PACK32 => Float,
+        vk::Format::A2R10G10B10_USCALED_PACK32 => Float,
+        vk::Format::A8B8G8R8_USCALED_PACK32 => Float,
+        vk::Format::B8G8R8A8_USCALED => Float,
+        vk::Format::B8G8R8_USCALED => Float,
+        vk::Format::R16G16B16A16_USCALED => Float,
+        vk::Format::R16G16B16_USCALED => Float,
+        vk::Format::R16G16_USCALED => Float,
+        vk::Format::R16_USCALED => Float,
+        vk::Format::R8G8B8A8_USCALED => Float,
+        vk::Format::R8G8B8_USCALED => Float,
+        vk::Format::R8G8_USCALED => Float,
+        vk::Format::R8_USCALED => Float,
+        _ => unimplemented!(),
+    }
+}
+
+impl FormatExt for vk::Format {
+    fn get_component_kind(&self) -> FormatComponentKind {
+        format_to_primitive(*self)
+    }
+    fn is_f32(&self) -> bool {
+        format_to_primitive(*self) == FormatComponentKind::Float
+    }
+
+    fn is_u32(&self) -> bool {
+        format_to_primitive(*self) == FormatComponentKind::UInt
+    }
+
+    fn is_i32(&self) -> bool {
+        format_to_primitive(*self) == FormatComponentKind::SInt
+    }
+}