From f1a3f64c93c9585338ac1a6aa1aafbcd94fbdaea Mon Sep 17 00:00:00 2001
From: Jens Koenen <koenen@vr.rwth-aachen.de>
Date: Thu, 6 Jan 2022 17:12:31 +0100
Subject: [PATCH] Worked on the encoder.

---
 liblava/frame/renderer.cpp |   14 +-
 liblava/frame/renderer.hpp |   15 +-
 src/encoder.cpp            | 2508 ++++++++++++++++++++----------------
 src/encoder.hpp            |  182 +--
 src/naive_stereo.cpp       |   12 +-
 src/stereo_strategy.cpp    |   10 +
 src/stereo_strategy.hpp    |    3 +
 src/vr_app.cpp             |    5 +
 src/vr_app.hpp             |    2 +
 9 files changed, 1532 insertions(+), 1219 deletions(-)

diff --git a/liblava/frame/renderer.cpp b/liblava/frame/renderer.cpp
index 88c354a8..0897a113 100644
--- a/liblava/frame/renderer.cpp
+++ b/liblava/frame/renderer.cpp
@@ -134,9 +134,11 @@ namespace lava {
         std::array<VkSemaphore, 1> const wait_semaphores = { image_acquired_semaphores[current_sync] };
         std::array<VkSemaphore, 1> const sync_present_semaphores = { render_complete_semaphores[current_sync] };
 
-        std::vector<VkSemaphore> complete_semaphores = frame_semaphores;
-        complete_semaphores.push_back(render_complete_semaphores[current_sync]);
-        frame_semaphores.clear();
+        std::vector<VkSemaphore> complete_semaphores = { render_complete_semaphores[current_sync] };
+        
+        for (frame_attachment& attachment : attachments) {
+            complete_semaphores.push_back(attachment.semaphore);
+        }
 
         VkPipelineStageFlags const wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
 
@@ -157,6 +159,12 @@ namespace lava {
         if (!device->vkQueueSubmit(graphics_queue.vk_queue, to_ui32(submit_infos.size()), submit_infos.data(), current_fence))
             return false;
 
+        for (frame_attachment& attachment : attachments) {
+            attachment.callback();
+        }
+
+        attachments.clear();
+
         std::array<VkSwapchainKHR, 1> const swapchains = { target->get() };
         std::array<ui32, 1> const indices = { current_frame };
 
diff --git a/liblava/frame/renderer.hpp b/liblava/frame/renderer.hpp
index c3e09614..de77c6be 100644
--- a/liblava/frame/renderer.hpp
+++ b/liblava/frame/renderer.hpp
@@ -11,6 +11,14 @@ namespace lava {
 
     using optional_index = std::optional<index>;
 
+    struct frame_attachment
+    {
+        VkSemaphore semaphore;
+        std::function<void(void)> callback;
+    };
+
+    using frame_attachments = std::vector<frame_attachment>;
+
     struct renderer : id_obj {
         bool create(swapchain* target);
         void destroy();
@@ -37,8 +45,8 @@ namespace lava {
             return graphics_queue;
         }
 
-        void add_semaphore(VkSemaphore semaphore){
-            frame_semaphores.push_back(semaphore);
+        void add_attachment(frame_attachment attachment) {
+            attachments.push_back(attachment);
         }
 
         using destroy_func = std::function<void()>;
@@ -60,7 +68,8 @@ namespace lava {
         VkFences fences_in_use = {};
         VkSemaphores image_acquired_semaphores = {};
         VkSemaphores render_complete_semaphores = {};
-        VkSemaphores frame_semaphores = {};
+
+        frame_attachments attachments = {};
     };
 
 } // namespace lava
diff --git a/src/encoder.cpp b/src/encoder.cpp
index d28da697..a5d55d07 100644
--- a/src/encoder.cpp
+++ b/src/encoder.cpp
@@ -5,1265 +5,1531 @@
 
 encoder_thread::encoder_thread(lava::device_ptr device, VkQueryPool query_pool, encoder_callback callback) : device(device), query_pool(query_pool), callback(callback)
 {
-	this->running = true;
-	this->thread = std::thread(&encoder_thread::process_frames, this);
+    this->running = true;
+    this->thread = std::thread(&encoder_thread::process_frames, this);
 }
 
 encoder_thread::~encoder_thread()
 {
-	this->running = false;
-	this->condition.notify_one();
-	this->thread.join();
+    this->running = false;
+    this->condition.notify_one();
+    this->thread.join();
 }
 
 encoder_thread::ptr encoder_thread::start_thread(lava::device_ptr device, VkQueryPool query_pool, encoder_callback callback)
 {
-	return std::make_shared<encoder_thread>(device, query_pool, callback);
+    return std::make_shared<encoder_thread>(device, query_pool, callback);
 }
 
 void encoder_thread::push_frame(encoder_frame::ptr frame)
 {
-	std::unique_lock<std::mutex> lock(this->mutex);
+    std::unique_lock<std::mutex> lock(this->mutex);
 
-	this->wait_list.push(frame);
-	this->condition.notify_one();
+    this->wait_list.push(frame);
+    this->condition.notify_one();
 }
 
 void encoder_thread::pop_frames(std::vector<encoder_frame::ptr>& frames)
 {
-	std::unique_lock<std::mutex> lock(this->mutex);
+    std::unique_lock<std::mutex> lock(this->mutex);
 
-	frames.insert(frames.end(), this->complete_list.begin(), this->complete_list.end());
-	this->complete_list.clear();
+    frames.insert(frames.end(), this->complete_list.begin(), this->complete_list.end());
+    this->complete_list.clear();
 }
 
 void encoder_thread::process_frames()
 {
-	while(true)
-	{
-		encoder_frame::ptr frame = nullptr;
+    while(true)
+    {
+        encoder_frame::ptr frame = nullptr;
 
-		{
-			std::unique_lock<std::mutex> lock(this->mutex);
+        {
+            std::unique_lock<std::mutex> lock(this->mutex);
 
-			while(this->wait_list.empty())
-			{
-				this->condition.wait(lock);
+            while(this->wait_list.empty())
+            {
+                this->condition.wait(lock);
 
-				if(!this->running)
-				{
-					return;
-				}
-			}
+                if(!this->running)
+                {
+                    return;
+                }
+            }
 
-			frame = this->wait_list.front();
-			this->wait_list.pop();
-		}
-		
-		if(vkWaitForFences(this->device->get(), 1, &frame->encode_fence, VK_TRUE, std::numeric_limits<uint64_t>::max()) != VK_SUCCESS)
-		{
-			return;
-		}
+            frame = this->wait_list.front();
+            this->wait_list.pop();
+        }
+        
+        if(vkWaitForFences(this->device->get(), 1, &frame->encode_fence, VK_TRUE, std::numeric_limits<uint64_t>::max()) != VK_SUCCESS)
+        {
+            return;
+        }
 
-		std::array<uint32_t, 2> buffer_range;
+        std::array<uint32_t, 2> buffer_range;
 
-		if(vkGetQueryPoolResults(this->device->get(), this->query_pool, frame->output_query_index, 1, sizeof(uint32_t) * buffer_range.size(), buffer_range.data(), 0, VK_QUERY_RESULT_WAIT_BIT) != VK_SUCCESS)
-		{
-			return;
-		}
+        if(vkGetQueryPoolResults(this->device->get(), this->query_pool, frame->output_query_index, 1, sizeof(uint32_t) * buffer_range.size(), buffer_range.data(), 0, VK_QUERY_RESULT_WAIT_BIT) != VK_SUCCESS)
+        {
+            return;
+        }
 
-		const uint32_t range_offset = buffer_range[0];
-		const uint32_t range_size = buffer_range[1];
+        const uint32_t range_offset = buffer_range[0];
+        const uint32_t range_size = buffer_range[1];
 
-		vmaInvalidateAllocation(device->alloc(), frame->output_buffer->get_allocation(), range_offset, range_size);
+        vmaInvalidateAllocation(device->alloc(), frame->output_buffer->get_allocation(), range_offset, range_size);
 
-		uint8_t* range_pointer = nullptr;
+        uint8_t* range_pointer = nullptr;
 
-		if(vmaMapMemory(device->alloc(), frame->output_buffer->get_allocation(), (void**)(&range_pointer)))
-		{
-			return;
-		}
+        if(vmaMapMemory(device->alloc(), frame->output_buffer->get_allocation(), (void**)(&range_pointer)))
+        {
+            return;
+        }
 
-		this->callback(range_pointer + range_offset, range_size);
+        this->callback(range_pointer + range_offset, range_size);
 
-		vmaUnmapMemory(device->alloc(), frame->output_buffer->get_allocation());
+        vmaUnmapMemory(device->alloc(), frame->output_buffer->get_allocation());
 
-		if(vkResetFences(this->device->get(), 1, &frame->encode_fence) != VK_SUCCESS)
-		{
-			return;
-		}
+        {
+            std::unique_lock<std::mutex> lock(this->mutex);
 
-		vkResetQueryPool(this->device->get(), this->query_pool, frame->output_query_index, 1);
-
-		{
-			std::unique_lock<std::mutex> lock(this->mutex);
-
-			this->complete_list.push_back(frame);
-		}
-	}
+            this->complete_list.push_back(frame);
+        }
+    }
 }
 
 // ----- encoder -------------------------------------------------------------------------------------------------------------
 
 bool encoder::create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, uint32_t frame_count, const encoder_callback& callback)
 {
-	this->default_queue = renderer.get_queue();
-
-	for(const lava::queue& queue : device->get_queues())
-	{
-		if((queue.flags & VK_QUEUE_VIDEO_ENCODE_BIT_KHR) == 0)
-		{
-			continue;
-		}
-
-		if(!this->check_encode_support(device, queue))
-		{
-			continue;
-		}
-
-		this->encode_queue = queue;
-
-		break;
-	}
-
-	if(!this->encode_queue.has_value())
-	{
-		return false;
-	}
-
-	if(!this->create_profiles(device))
-	{
-		return false;
-	}
-
-	if(!this->check_format_support(device, VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR, VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT))
-	{
-		return false;
-	}
-
-	if(!this->check_format_support(device, VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR, VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT))
-	{
-		return false;
-	}
-
-	if(!this->create_session(device, size))
-	{
-		return false;
-	}
-
-	if(!this->bind_session_memory(device))
-	{
-		return false;
-	}
-
-	if(!this->create_parameters(device, size))
-	{
-		return false;
-	}
-
-	if(!this->create_sampler(device))
-	{
-		return false;
-	}
-
-	if(!this->create_pools(device, frame_count))
-	{
-		return false;
-	}
-
-	if(!this->create_layouts(device))
-	{
-		return false;
-	}
-
-	for(uint32_t index = 0; index < frame_count; index++)
-	{
-		encoder_frame::ptr frame = std::make_shared<encoder_frame>();
-		frame->output_query_index = index;
-
-		if(!this->create_input_images(device, frame, size))
-		{
-			return false;
-		}
-
-		if(!this->create_output_buffer(device, frame))
-		{
-			return false;
-		}
-
-		if(!this->create_convert_pass(device, frame, size))
-		{
-			return false;
-		}
-
-		if(!this->create_convert_pipeline(device, frame))
-		{
-			return false;
-		}
-
-		if(!this->create_encode_resources(device, frame))
-		{
-			return false;
-		}
-
-		this->frame_list.push_back(frame);
-	}
-
-	this->size = size;
-	this->frame_count = frame_count;
-	this->callback = callback;
-	this->thread = encoder_thread::start_thread(device, this->query_pool, callback);
-
-	return true;
+    this->default_queue = renderer.get_queue();
+
+    for(const lava::queue& queue : device->get_queues())
+    {
+        if((queue.flags & VK_QUEUE_VIDEO_ENCODE_BIT_KHR) == 0)
+        {
+            continue;
+        }
+
+        if(!this->check_encode_support(device, queue))
+        {
+            continue;
+        }
+
+        this->encode_queue = queue;
+
+        break;
+    }
+
+    if(!this->encode_queue.has_value())
+    {
+        return false;
+    }
+
+    if(!this->create_profiles(device))
+    {
+        return false;
+    }
+
+    if(!this->check_format_support(device, VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR, VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT))
+    {
+        return false;
+    }
+
+    if(!this->check_format_support(device, VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR, VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT))
+    {
+        return false;
+    }
+
+    if(!this->create_session(device, size))
+    {
+        return false;
+    }
+
+    if(!this->bind_session_memory(device))
+    {
+        return false;
+    }
+
+    if(!this->create_parameters(device, size))
+    {
+        return false;
+    }
+
+    if(!this->create_sampler(device))
+    {
+        return false;
+    }
+
+    if(!this->create_pools(device, frame_count))
+    {
+        return false;
+    }
+
+    if(!this->create_layouts(device))
+    {
+        return false;
+    }
+
+    for(uint32_t index = 0; index < frame_count; index++)
+    {
+        encoder_frame::ptr frame = std::make_shared<encoder_frame>();
+        frame->output_query_index = index;
+
+        if(!this->create_input_images(device, frame, size))
+        {
+            return false;
+        }
+
+        if(!this->create_slot_image(device, frame, size))
+        {
+            return false;
+        }
+
+        if(!this->create_output_buffer(device, frame))
+        {
+            return false;
+        }
+
+        if(!this->create_convert_pass(device, frame, size))
+        {
+            return false;
+        }
+
+        if(!this->create_convert_pipeline(device, frame))
+        {
+            return false;
+        }
+
+        if(!this->create_encode_resources(device, frame, size))
+        {
+            return false;
+        }
+
+        this->frame_list.push_back(frame);
+    }
+
+    this->size = size;
+    this->frame_count = frame_count;
+    this->frame_index = 0;
+    this->callback = callback;
+    this->thread = encoder_thread::start_thread(device, this->query_pool, callback);
+
+    return true;
 }
 
 void encoder::destroy()
 {
-	//TODO: First, wait for the encoder_thread to complete
-	//TODO: Destroy all objects
+    //TODO: First, wait for the encoder_thread to complete
+    //TODO: Destroy all objects
 }
 
 bool encoder::encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout)
 {
-	lava::device_ptr device = image->get_device();
-	encoder_frame::ptr frame = nullptr;
-
-	if(!this->aquire_frame(frame))
-	{
-		return false;
-	}
-
-	if(image_layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
-	{
-		lava::insert_image_memory_barrier(device, command_buffer, image->get(), 0, VK_ACCESS_SHADER_READ_BIT, image_layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, image->get_subresource_range());
-	}
-
-	this->write_image(device, frame, image);
-
-	vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, this->pipeline_layout->get(), 0, 1, &frame->convert_descriptor, 0, nullptr);
-	frame->convert_pass->process(command_buffer, 0);
-
-	if(image_layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
-	{
-		lava::insert_image_memory_barrier(device, command_buffer, image->get(), VK_ACCESS_SHADER_READ_BIT, 0, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, image_layout, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, image->get_subresource_range());
-	}
-
-	VkImageSubresourceRange image_range;
-	image_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-	image_range.baseMipLevel = 0;
-	image_range.levelCount = 1;
-	image_range.baseArrayLayer = 0;
-	image_range.layerCount = 1;
-
-	VkImageMemoryBarrier image_barrier;
-	image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
-	image_barrier.pNext = nullptr;
-	image_barrier.srcAccessMask = 0;
-	image_barrier.dstAccessMask = 0;
-	image_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
-	image_barrier.newLayout = VK_IMAGE_LAYOUT_VIDEO_ENCODE_SRC_KHR;
-	image_barrier.srcQueueFamilyIndex = this->default_queue.family;
-	image_barrier.dstQueueFamilyIndex = this->encode_queue->family;
-	image_barrier.image = frame->input_combined;
-	image_barrier.subresourceRange = image_range;
-
-	vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_barrier);
-
-	return this->submit_frame(device, frame, renderer);
+    lava::device_ptr device = image->get_device();
+    encoder_frame::ptr frame = nullptr;
+
+    if(!this->aquire_frame(frame))
+    {
+        return false;
+    }
+
+    if(image_layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
+    {
+        lava::insert_image_memory_barrier(device, command_buffer, image->get(), 0, VK_ACCESS_SHADER_READ_BIT, image_layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, image->get_subresource_range());
+    }
+
+    this->write_image(device, frame, image);
+
+    vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, this->pipeline_layout->get(), 0, 1, &frame->convert_descriptor, 0, nullptr);
+    frame->convert_pass->process(command_buffer, 0);
+
+    if(image_layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
+    {
+        lava::insert_image_memory_barrier(device, command_buffer, image->get(), VK_ACCESS_SHADER_READ_BIT, 0, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, image_layout, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, image->get_subresource_range());
+    }
+
+    VkImageSubresourceRange image_range;
+    image_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    image_range.baseMipLevel = 0;
+    image_range.levelCount = 1;
+    image_range.baseArrayLayer = 0;
+    image_range.layerCount = 1;
+
+    VkImageMemoryBarrier image_barrier;
+    image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+    image_barrier.pNext = nullptr;
+    image_barrier.srcAccessMask = 0;
+    image_barrier.dstAccessMask = 0;
+    image_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
+    image_barrier.newLayout = VK_IMAGE_LAYOUT_VIDEO_ENCODE_SRC_KHR;
+    image_barrier.srcQueueFamilyIndex = this->default_queue.family;
+    image_barrier.dstQueueFamilyIndex = this->encode_queue->family;
+    image_barrier.image = frame->input_combined;
+    image_barrier.subresourceRange = image_range;
+
+    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_barrier);
+
+    if(!this->record_encoding(device, frame))
+    {
+        return false;
+    }
+
+    if(!this->submit_frame(device, frame, renderer))
+    {
+        return false;
+    }
+
+    this->frame_index++;
+    
+    return true;
 }
 
 bool encoder::aquire_frame(encoder_frame::ptr& frame)
 {
-	this->thread->pop_frames(this->frame_list);
+    this->thread->pop_frames(this->frame_list);
 
-	if(!this->frame_list.empty())
-	{
-		frame = this->frame_list.back();
-		this->frame_list.pop_back();
+    if(!this->frame_list.empty())
+    {
+        frame = this->frame_list.back();
+        this->frame_list.pop_back();
 
-		return true;
-	}
+        return true;
+    }
 
-	return false;
+    return false;
 }
 
 bool encoder::submit_frame(lava::device_ptr device, encoder_frame::ptr frame, lava::renderer& renderer)
 {
-	VkPipelineStageFlags stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
-
-	VkSubmitInfo submit_info;
-	submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
-	submit_info.pNext = nullptr;
-	submit_info.waitSemaphoreCount = 1;
-	submit_info.pWaitSemaphores = &frame->encode_semaphore;
-	submit_info.pWaitDstStageMask = &stage;
-	submit_info.commandBufferCount = 1;
-	submit_info.pCommandBuffers = &frame->encode_command_buffer;
-	submit_info.signalSemaphoreCount = 0;
-	submit_info.pSignalSemaphores = nullptr;
-
-	if(vkQueueSubmit(this->encode_queue->vk_queue, 1, &submit_info, frame->encode_fence) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	renderer.add_semaphore(frame->encode_semaphore);
-
-	this->thread->push_frame(frame);
-
-	return true;
+    lava::frame_attachment attachment;
+    attachment.semaphore = frame->encode_semaphore;
+    attachment.callback = [=]()
+    {
+        VkPipelineStageFlags stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+
+        VkSubmitInfo submit_info;
+        submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+        submit_info.pNext = nullptr;
+        submit_info.waitSemaphoreCount = 1;
+        submit_info.pWaitSemaphores = &frame->encode_semaphore;
+        submit_info.pWaitDstStageMask = &stage;
+        submit_info.commandBufferCount = 1;
+        submit_info.pCommandBuffers = &frame->encode_command_buffer;
+        submit_info.signalSemaphoreCount = 0;
+        submit_info.pSignalSemaphores = nullptr;
+
+        vkQueueSubmit(this->encode_queue->vk_queue, 1, &submit_info, frame->encode_fence); //TODO: Error check
+
+        this->thread->push_frame(frame);
+    };
+
+    renderer.add_attachment(attachment);
+
+    return true;
 }
 
 void encoder::write_image(lava::device_ptr device, encoder_frame::ptr frame, lava::image::ptr image)
 {
-	VkDescriptorImageInfo image_info;
-	image_info.sampler = this->sampler;
-	image_info.imageView = image->get_view();
-	image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
-
-	VkWriteDescriptorSet write_info;
-	write_info.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-	write_info.pNext = nullptr;
-	write_info.dstSet = frame->convert_descriptor;
-	write_info.dstBinding = 0;
-	write_info.dstArrayElement = 0;
-	write_info.descriptorCount = 1;
-	write_info.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-	write_info.pImageInfo = &image_info;
-	write_info.pBufferInfo = nullptr;
-	write_info.pTexelBufferView = nullptr;
-
-	vkUpdateDescriptorSets(device->get(), 1, &write_info, 0, nullptr);
+    VkDescriptorImageInfo image_info;
+    image_info.sampler = this->sampler;
+    image_info.imageView = image->get_view();
+    image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+    VkWriteDescriptorSet write_info;
+    write_info.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+    write_info.pNext = nullptr;
+    write_info.dstSet = frame->convert_descriptor;
+    write_info.dstBinding = 0;
+    write_info.dstArrayElement = 0;
+    write_info.descriptorCount = 1;
+    write_info.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+    write_info.pImageInfo = &image_info;
+    write_info.pBufferInfo = nullptr;
+    write_info.pTexelBufferView = nullptr;
+
+    vkUpdateDescriptorSets(device->get(), 1, &write_info, 0, nullptr);
+}
+
+bool encoder::record_encoding(lava::device_ptr device, encoder_frame::ptr frame)
+{
+    if(vkResetFences(device->get(), 1, &frame->encode_fence) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    if(vkResetCommandBuffer(frame->encode_command_buffer, 0) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    VkCommandBufferBeginInfo buffer_begin_info;
+    buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+    buffer_begin_info.pNext = nullptr;
+    buffer_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+    buffer_begin_info.pInheritanceInfo = nullptr;
+
+    if(vkBeginCommandBuffer(frame->encode_command_buffer, &buffer_begin_info) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    this->encode_pass_setup(frame->encode_command_buffer, frame);
+
+    if(vkEndCommandBuffer(frame->encode_command_buffer) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    return true;
 }
 
 bool encoder::create_profiles(lava::device_ptr device)
 {
-	this->encode_profile.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PROFILE_EXT;
-	this->encode_profile.pNext = nullptr;
-	this->encode_profile.stdProfileIdc = std_video_h264_profile_idc_baseline;
-
-	this->video_profile.sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_KHR;
-	this->video_profile.pNext = &this->encode_profile;
-	this->video_profile.videoCodecOperation = VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_EXT;
-	this->video_profile.chromaSubsampling = VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR; //No subsampling
-	this->video_profile.lumaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR;
-	this->video_profile.chromaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR;
-
-	this->encode_capabillities.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_CAPABILITIES_EXT;
-	this->encode_capabillities.pNext = nullptr;
-	this->video_capabillities.sType = VK_STRUCTURE_TYPE_VIDEO_CAPABILITIES_KHR;
-	this->video_capabillities.pNext = &this->encode_capabillities;
-
-	if(vkGetPhysicalDeviceVideoCapabilitiesKHR(device->get_physical_device()->get(), &this->video_profile, &this->video_capabillities) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	return true;
+    this->encode_profile.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PROFILE_EXT;
+    this->encode_profile.pNext = nullptr;
+    this->encode_profile.stdProfileIdc = std_video_h264_profile_idc_baseline;
+
+    this->video_profile.sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_KHR;
+    this->video_profile.pNext = &this->encode_profile;
+    this->video_profile.videoCodecOperation = VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_EXT;
+    this->video_profile.chromaSubsampling = VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR; //No subsampling
+    this->video_profile.lumaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR;
+    this->video_profile.chromaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR;
+
+    this->encode_capabillities.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_CAPABILITIES_EXT;
+    this->encode_capabillities.pNext = nullptr;
+    this->video_capabillities.sType = VK_STRUCTURE_TYPE_VIDEO_CAPABILITIES_KHR;
+    this->video_capabillities.pNext = &this->encode_capabillities;
+
+    if(vkGetPhysicalDeviceVideoCapabilitiesKHR(device->get_physical_device()->get(), &this->video_profile, &this->video_capabillities) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    return true;
 }
 
 bool encoder::create_session(lava::device_ptr device, const glm::uvec2& size)
 {
-	VkExtent2D video_extend;
-	video_extend.width = size.x;
-	video_extend.height = size.y;
-
-	VkExtent2D macro_block_extend;
-	macro_block_extend.width = (video_extend.width / this->video_capabillities.videoPictureExtentGranularity.width) + 1;
-	macro_block_extend.height = (video_extend.height / this->video_capabillities.videoPictureExtentGranularity.height) + 1;
-
-	VkVideoEncodeH264SessionCreateInfoEXT encode_create_info;
-	encode_create_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_CREATE_INFO_EXT;
-	encode_create_info.pNext = nullptr;
-	encode_create_info.flags = VK_VIDEO_ENCODE_H264_CREATE_RESERVED_0_BIT_EXT; //not 0 ???
-	encode_create_info.maxPictureSizeInMbs = macro_block_extend;
-	encode_create_info.pStdExtensionVersion = &this->encode_capabillities.stdExtensionVersion;
-
-	VkVideoSessionCreateInfoKHR video_create_info;
-	video_create_info.sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_CREATE_INFO_KHR;
-	video_create_info.pNext = &encode_create_info;
-	video_create_info.queueFamilyIndex = this->encode_queue->family;
-	video_create_info.flags = VK_VIDEO_SESSION_CREATE_DEFAULT_KHR;
-	video_create_info.pVideoProfile = &this->video_profile;
-	video_create_info.pictureFormat = VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT;
-	video_create_info.maxCodedExtent = video_extend;
-	video_create_info.referencePicturesFormat = VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT;
-	video_create_info.maxReferencePicturesSlotsCount = 1;
-	video_create_info.maxReferencePicturesActiveCount = 1;
-
-	if(vkCreateVideoSessionKHR(device->get(), &video_create_info, lava::memory::alloc(), &this->video_session) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	return true;
+    VkExtent2D video_extend;
+    video_extend.width = size.x;
+    video_extend.height = size.y;
+
+    VkExtent2D macro_block_extend;
+    macro_block_extend.width = (video_extend.width / this->video_capabillities.videoPictureExtentGranularity.width) + 1;
+    macro_block_extend.height = (video_extend.height / this->video_capabillities.videoPictureExtentGranularity.height) + 1;
+
+    VkVideoEncodeH264SessionCreateInfoEXT encode_create_info;
+    encode_create_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_CREATE_INFO_EXT;
+    encode_create_info.pNext = nullptr;
+    encode_create_info.flags = VK_VIDEO_ENCODE_H264_CREATE_RESERVED_0_BIT_EXT; //not 0 ???
+    encode_create_info.maxPictureSizeInMbs = macro_block_extend;
+    encode_create_info.pStdExtensionVersion = &this->encode_capabillities.stdExtensionVersion;
+
+    VkVideoSessionCreateInfoKHR video_create_info;
+    video_create_info.sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_CREATE_INFO_KHR;
+    video_create_info.pNext = &encode_create_info;
+    video_create_info.queueFamilyIndex = this->encode_queue->family;
+    video_create_info.flags = VK_VIDEO_SESSION_CREATE_DEFAULT_KHR;
+    video_create_info.pVideoProfile = &this->video_profile;
+    video_create_info.pictureFormat = VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT;
+    video_create_info.maxCodedExtent = video_extend;
+    video_create_info.referencePicturesFormat = VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT;
+    video_create_info.maxReferencePicturesSlotsCount = 1;
+    video_create_info.maxReferencePicturesActiveCount = 1;
+
+    if(vkCreateVideoSessionKHR(device->get(), &video_create_info, lava::memory::alloc(), &this->video_session) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    return true;
 }
 
 bool encoder::create_parameters(lava::device_ptr device, const glm::uvec2& size)
 {
-	glm::uvec2 block_size;
-	block_size.x = this->video_capabillities.videoPictureExtentGranularity.width;
-	block_size.y = this->video_capabillities.videoPictureExtentGranularity.height;
-
-	glm::uvec2 block_count_minus_one = size / block_size;
-	glm::uvec2 frame_crop = block_size - (size % block_size);
-
-	//H.264 Specification Section 7.4.2.1.1: 
-	//https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-H.264-202108-I!!PDF-E&type=items
-	StdVideoH264SpsFlags sps_flags;
-	sps_flags.constraint_set0_flag = 0;											//No constraints
-	sps_flags.constraint_set1_flag = 0;											//No constraints
-	sps_flags.constraint_set2_flag = 0;											//No constraints
-	sps_flags.constraint_set3_flag = 0;											//No constraints
-	sps_flags.constraint_set4_flag = 0;											//No constraints
-	sps_flags.constraint_set5_flag = 0;											//No constraints
-	sps_flags.direct_8x8_inference_flag = 0;									//Only for b-frames set it to zero
-	sps_flags.mb_adaptive_frame_field_flag = 0;									//Set it to zero										
-	sps_flags.frame_mbs_only_flag = 1;											//Only frames no fields
-	sps_flags.delta_pic_order_always_zero_flag = 0;								//Set it to zero										
-	sps_flags.residual_colour_transform_flag = 0;								//Combined encoding see separate_colour_plane_flag
-	sps_flags.gaps_in_frame_num_value_allowed_flag = 0;							//No gaps
-	sps_flags.first_picture_after_seek_flag = 0;								//Where is this being documented? 
-	sps_flags.qpprime_y_zero_transform_bypass_flag = 0;							//Set it to zero										
-	sps_flags.frame_cropping_flag =	1;											//Use crop to adjust the image size
-	sps_flags.scaling_matrix_present_flag = 0;									//Dont send scaling matrix
-	sps_flags.vui_parameters_present_flag = 0;									//Dont send VUI
-
-	StdVideoH264SequenceParameterSet sps_parameter;
-	sps_parameter.profile_idc = std_video_h264_profile_idc_baseline;			//Baseline profile
-	sps_parameter.level_idc = std_video_h264_level_1_0;							//Level 1.0
-	sps_parameter.seq_parameter_set_id = 0;										//Id of the sequence parameter set
-	sps_parameter.chroma_format_idc = std_video_h264_chroma_format_idc_444;		//Chroma format 444 meaning same image size for chroma and luma
-	sps_parameter.bit_depth_luma_minus8 = 0;									//8 bit luma
-	sps_parameter.bit_depth_chroma_minus8 = 0;									//8 bit chroma
-	sps_parameter.log2_max_frame_num_minus4 = 0;								//2^4 frame numbers
-	sps_parameter.pic_order_cnt_type = std_video_h264_poc_type_0;				//Picture order count type 0
-	sps_parameter.log2_max_pic_order_cnt_lsb_minus4 = 0;						//Set to zero
-	sps_parameter.offset_for_non_ref_pic = 0;									//Set to zero
-	sps_parameter.offset_for_top_to_bottom_field = 0;							//Set to zero
-	sps_parameter.num_ref_frames_in_pic_order_cnt_cycle = 0;					//Set to zero
-	sps_parameter.max_num_ref_frames = 0;										//0 Reference frames therefore only i-frames
-	sps_parameter.pic_width_in_mbs_minus1 = block_count_minus_one.x;			//Image width in macroblocks
-	sps_parameter.pic_height_in_map_units_minus1 = block_count_minus_one.y;		//Image height in macroblocks
-	sps_parameter.frame_crop_left_offset = 0;									//No crop left
-	sps_parameter.frame_crop_right_offset = frame_crop.x;						//Crop right to get the correct frame width
-	sps_parameter.frame_crop_top_offset = 0;									//No crop top
-	sps_parameter.frame_crop_bottom_offset = frame_crop.y;						//Crop bottom to get the correct frame height
-	sps_parameter.flags = sps_flags;
-	sps_parameter.offset_for_ref_frame[0];										//Not send
-	sps_parameter.pScalingLists = nullptr;										//Not send
-	sps_parameter.pSequenceParameterSetVui = nullptr;							//Not send
-	
-	//H.264 Specification Section 7.4.2.2:
-	StdVideoH264PpsFlags pps_flags;
-	pps_flags.transform_8x8_mode_flag = 0;										//Set to zero
-	pps_flags.redundant_pic_cnt_present_flag = 0;								//Set to zero
-	pps_flags.constrained_intra_pred_flag = 0;									//Set to zero
-	pps_flags.deblocking_filter_control_present_flag = 0;						//No deblocking
-	pps_flags.weighted_bipred_idc_flag = 0;										//Set to zero
-	pps_flags.weighted_pred_flag = 0;											//Set to zero
-	pps_flags.pic_order_present_flag = 0;										//Set to zero
-	pps_flags.entropy_coding_mode_flag = 0;										//Use CABAC for encoding
-	pps_flags.scaling_matrix_present_flag = 0;									//Dont send scaling matrix
-
-	StdVideoH264PictureParameterSet pps_parameter;
-	pps_parameter.seq_parameter_set_id = 0;										//Id of the sequence parameter set that is referenced
-	pps_parameter.pic_parameter_set_id = 0;										//Id of the picture parameter set										
-	pps_parameter.num_ref_idx_l0_default_active_minus1 = 0;						//Set to zero
-	pps_parameter.num_ref_idx_l1_default_active_minus1 = 0;						//Set to zero
-	pps_parameter.weighted_bipred_idc = std_video_h264_default_weighted_b_slices_prediction_idc; //Set to default
-	pps_parameter.pic_init_qp_minus26 = 0;										//Set to zero
-	pps_parameter.pic_init_qs_minus26 = 0;										//Set to zero
-	pps_parameter.chroma_qp_index_offset = 0;									//Set to zero
-	pps_parameter.second_chroma_qp_index_offset = 0;							//Set to zero
-	pps_parameter.flags = pps_flags;
-	pps_parameter.pScalingLists = nullptr;										//Not send
-
-	VkVideoEncodeH264SessionParametersAddInfoEXT add_info;
-	add_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT;
-	add_info.pNext = nullptr;
-	add_info.spsStdCount = 1;
-	add_info.pSpsStd = &sps_parameter;
-	add_info.ppsStdCount = 1;
-	add_info.pPpsStd = &pps_parameter;
-
-	VkVideoEncodeH264SessionParametersCreateInfoEXT encode_create_info;
-	encode_create_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT;
-	encode_create_info.pNext = nullptr;
-	encode_create_info.maxSpsStdCount = 1;
-	encode_create_info.maxPpsStdCount = 1;
-	encode_create_info.pParametersAddInfo = &add_info;
-
-	VkVideoSessionParametersCreateInfoKHR video_create_info;
-	video_create_info.sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR;
-	video_create_info.pNext = &encode_create_info;
-	video_create_info.videoSessionParametersTemplate = nullptr;
-	video_create_info.videoSession = this->video_session;
-
-	if(vkCreateVideoSessionParametersKHR(device->get(), &video_create_info, lava::memory::alloc(), &this->video_session_paremeters) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	return true;
+    glm::uvec2 block_size;
+    block_size.x = this->video_capabillities.videoPictureExtentGranularity.width;
+    block_size.y = this->video_capabillities.videoPictureExtentGranularity.height;
+
+    glm::uvec2 block_count_minus_one = size / block_size;
+    glm::uvec2 frame_crop = block_size - (size % block_size);
+
+    //H.264 Specification Section 7.4.2.1.1: 
+    //https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-H.264-202108-I!!PDF-E&type=items
+    StdVideoH264SpsFlags sps_flags;
+    sps_flags.constraint_set0_flag = 0;                                            //No constraints
+    sps_flags.constraint_set1_flag = 0;                                            //No constraints
+    sps_flags.constraint_set2_flag = 0;                                            //No constraints
+    sps_flags.constraint_set3_flag = 0;                                            //No constraints
+    sps_flags.constraint_set4_flag = 0;                                            //No constraints
+    sps_flags.constraint_set5_flag = 0;                                            //No constraints
+    sps_flags.direct_8x8_inference_flag = 0;                                       //Only for b-frames set it to zero
+    sps_flags.mb_adaptive_frame_field_flag = 0;                                    //Set it to zero                                        
+    sps_flags.frame_mbs_only_flag = 1;                                             //Only frames no fields
+    sps_flags.delta_pic_order_always_zero_flag = 0;                                //Set it to zero                                        
+    sps_flags.residual_colour_transform_flag = 0;                                  //Combined encoding see separate_colour_plane_flag
+    sps_flags.gaps_in_frame_num_value_allowed_flag = 0;                            //No gaps
+    sps_flags.first_picture_after_seek_flag = 0;                                   //Where is this being documented? 
+    sps_flags.qpprime_y_zero_transform_bypass_flag = 0;                            //Set it to zero                                        
+    sps_flags.frame_cropping_flag = 1;                                             //Use crop to adjust the image size
+    sps_flags.scaling_matrix_present_flag = 0;                                     //Dont send scaling matrix
+    sps_flags.vui_parameters_present_flag = 0;                                     //Dont send VUI
+
+    StdVideoH264SequenceParameterSet sps_parameter;
+    sps_parameter.profile_idc = std_video_h264_profile_idc_baseline;               //Baseline profile
+    sps_parameter.level_idc = std_video_h264_level_1_0;                            //Level 1.0
+    sps_parameter.seq_parameter_set_id = 0;                                        //Id of the sequence parameter set
+    sps_parameter.chroma_format_idc = std_video_h264_chroma_format_idc_444;        //Chroma format 444 meaning same image size for chroma and luma
+    sps_parameter.bit_depth_luma_minus8 = 0;                                       //8 bit luma
+    sps_parameter.bit_depth_chroma_minus8 = 0;                                     //8 bit chroma
+    sps_parameter.log2_max_frame_num_minus4 = 0;                                   //2^4 frame numbers
+    sps_parameter.pic_order_cnt_type = std_video_h264_poc_type_0;                  //Picture order count type 0
+    sps_parameter.log2_max_pic_order_cnt_lsb_minus4 = 0;                           //Set to zero
+    sps_parameter.offset_for_non_ref_pic = 0;                                      //Set to zero
+    sps_parameter.offset_for_top_to_bottom_field = 0;                              //Set to zero
+    sps_parameter.num_ref_frames_in_pic_order_cnt_cycle = 0;                       //Set to zero
+    sps_parameter.max_num_ref_frames = 0;                                          //0 Reference frames therefore only i-frames
+    sps_parameter.pic_width_in_mbs_minus1 = block_count_minus_one.x;               //Image width in macroblocks
+    sps_parameter.pic_height_in_map_units_minus1 = block_count_minus_one.y;        //Image height in macroblocks
+    sps_parameter.frame_crop_left_offset = 0;                                      //No crop left
+    sps_parameter.frame_crop_right_offset = frame_crop.x;                          //Crop right to get the correct frame width
+    sps_parameter.frame_crop_top_offset = 0;                                       //No crop top
+    sps_parameter.frame_crop_bottom_offset = frame_crop.y;                         //Crop bottom to get the correct frame height
+    sps_parameter.flags = sps_flags;
+    sps_parameter.offset_for_ref_frame[0];                                         //Not send
+    sps_parameter.pScalingLists = nullptr;                                         //Not send
+    sps_parameter.pSequenceParameterSetVui = nullptr;                              //Not send
+    
+    //H.264 Specification Section 7.4.2.2:
+    StdVideoH264PpsFlags pps_flags;
+    pps_flags.transform_8x8_mode_flag = 0;                                         //Set to zero
+    pps_flags.redundant_pic_cnt_present_flag = 0;                                  //Set to zero
+    pps_flags.constrained_intra_pred_flag = 0;                                     //Set to zero
+    pps_flags.deblocking_filter_control_present_flag = 0;                          //No deblocking
+    pps_flags.weighted_bipred_idc_flag = 0;                                        //Set to zero
+    pps_flags.weighted_pred_flag = 0;                                              //Set to zero
+    pps_flags.pic_order_present_flag = 0;                                          //Set to zero
+    pps_flags.entropy_coding_mode_flag = 0;                                        //Use CABAC for encoding
+    pps_flags.scaling_matrix_present_flag = 0;                                     //Dont send scaling matrix
+
+    StdVideoH264PictureParameterSet pps_parameter;
+    pps_parameter.seq_parameter_set_id = 0;                                        //Id of the sequence parameter set that is referenced
+    pps_parameter.pic_parameter_set_id = 0;                                        //Id of the picture parameter set                                        
+    pps_parameter.num_ref_idx_l0_default_active_minus1 = 0;                        //Set to zero
+    pps_parameter.num_ref_idx_l1_default_active_minus1 = 0;                        //Set to zero
+    pps_parameter.weighted_bipred_idc = std_video_h264_default_weighted_b_slices_prediction_idc; //Set to default
+    pps_parameter.pic_init_qp_minus26 = 0;                                         //Set to zero
+    pps_parameter.pic_init_qs_minus26 = 0;                                         //Set to zero
+    pps_parameter.chroma_qp_index_offset = 0;                                      //Set to zero
+    pps_parameter.second_chroma_qp_index_offset = 0;                               //Set to zero
+    pps_parameter.flags = pps_flags;
+    pps_parameter.pScalingLists = nullptr;                                         //Not send
+
+    VkVideoEncodeH264SessionParametersAddInfoEXT add_info;
+    add_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT;
+    add_info.pNext = nullptr;
+    add_info.spsStdCount = 1;
+    add_info.pSpsStd = &sps_parameter;
+    add_info.ppsStdCount = 1;
+    add_info.pPpsStd = &pps_parameter;
+
+    VkVideoEncodeH264SessionParametersCreateInfoEXT encode_create_info;
+    encode_create_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT;
+    encode_create_info.pNext = nullptr;
+    encode_create_info.maxSpsStdCount = 1;
+    encode_create_info.maxPpsStdCount = 1;
+    encode_create_info.pParametersAddInfo = &add_info;
+
+    VkVideoSessionParametersCreateInfoKHR video_create_info;
+    video_create_info.sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR;
+    video_create_info.pNext = &encode_create_info;
+    video_create_info.videoSessionParametersTemplate = nullptr;
+    video_create_info.videoSession = this->video_session;
+
+    if(vkCreateVideoSessionParametersKHR(device->get(), &video_create_info, lava::memory::alloc(), &this->video_session_paremeters) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    return true;
 }
 
 bool encoder::bind_session_memory(lava::device_ptr device)
 {
-	uint32_t property_count = 0;
-	if(vkGetVideoSessionMemoryRequirementsKHR(device->get(), this->video_session, &property_count, nullptr) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	std::vector<VkMemoryRequirements2> requirement_list;
-	requirement_list.resize(property_count);
-	std::vector<VkVideoGetMemoryPropertiesKHR> property_list;
-	property_list.resize(property_count);
-
-	for(uint32_t index = 0; index < property_count; index++)
-	{
-		requirement_list[index].sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
-		requirement_list[index].pNext = nullptr;
-		property_list[index].sType = VK_STRUCTURE_TYPE_VIDEO_GET_MEMORY_PROPERTIES_KHR;
-		property_list[index].pNext = nullptr;
-		property_list[index].pMemoryRequirements = &requirement_list[index];
-	}
-
-	if(vkGetVideoSessionMemoryRequirementsKHR(device->get(), this->video_session, &property_count, property_list.data()) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	std::vector<VkVideoBindMemoryKHR> bind_list;
-
-	for(const VkVideoGetMemoryPropertiesKHR& property : property_list)
-	{
-		VmaAllocationCreateInfo create_info;
-		create_info.flags = 0;
-		create_info.usage = VMA_MEMORY_USAGE_UNKNOWN;
-		create_info.requiredFlags = 0;
-		create_info.preferredFlags = 0;
-		create_info.memoryTypeBits = 0;
-		create_info.pool = nullptr;
-		create_info.pUserData = nullptr;
-		create_info.priority = 1.0f;
-
-		VmaAllocation memory;
-		VmaAllocationInfo info;
-		if(vmaAllocateMemory(device->alloc(), &property.pMemoryRequirements->memoryRequirements, &create_info, &memory, &info) != VK_SUCCESS)
-		{
-			return false;
-		}
-
-		this->video_session_memory.push_back(memory);
-
-		VkVideoBindMemoryKHR bind;
-		bind.sType = VK_STRUCTURE_TYPE_VIDEO_BIND_MEMORY_KHR;
-		bind.pNext = nullptr;
-		bind.memoryBindIndex = property.memoryBindIndex;
-		bind.memory = info.deviceMemory;
-		bind.memoryOffset = info.offset;
-		bind.memorySize = info.size;
-
-		bind_list.push_back(bind);
-	}
-
-	if(vkBindVideoSessionMemoryKHR(device->get(), this->video_session, bind_list.size(), bind_list.data()) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	return true;
+    uint32_t property_count = 0;
+    if(vkGetVideoSessionMemoryRequirementsKHR(device->get(), this->video_session, &property_count, nullptr) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    std::vector<VkMemoryRequirements2> requirement_list;
+    requirement_list.resize(property_count);
+    std::vector<VkVideoGetMemoryPropertiesKHR> property_list;
+    property_list.resize(property_count);
+
+    for(uint32_t index = 0; index < property_count; index++)
+    {
+        requirement_list[index].sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
+        requirement_list[index].pNext = nullptr;
+        property_list[index].sType = VK_STRUCTURE_TYPE_VIDEO_GET_MEMORY_PROPERTIES_KHR;
+        property_list[index].pNext = nullptr;
+        property_list[index].pMemoryRequirements = &requirement_list[index];
+    }
+
+    if(vkGetVideoSessionMemoryRequirementsKHR(device->get(), this->video_session, &property_count, property_list.data()) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    std::vector<VkVideoBindMemoryKHR> bind_list;
+
+    for(const VkVideoGetMemoryPropertiesKHR& property : property_list)
+    {
+        VmaAllocationCreateInfo create_info;
+        create_info.flags = 0;
+        create_info.usage = VMA_MEMORY_USAGE_UNKNOWN;
+        create_info.requiredFlags = 0;
+        create_info.preferredFlags = 0;
+        create_info.memoryTypeBits = 0;
+        create_info.pool = nullptr;
+        create_info.pUserData = nullptr;
+        create_info.priority = 1.0f;
+
+        VmaAllocation memory;
+        VmaAllocationInfo info;
+        if(vmaAllocateMemory(device->alloc(), &property.pMemoryRequirements->memoryRequirements, &create_info, &memory, &info) != VK_SUCCESS)
+        {
+            return false;
+        }
+
+        this->video_session_memory.push_back(memory);
+
+        VkVideoBindMemoryKHR bind;
+        bind.sType = VK_STRUCTURE_TYPE_VIDEO_BIND_MEMORY_KHR;
+        bind.pNext = nullptr;
+        bind.memoryBindIndex = property.memoryBindIndex;
+        bind.memory = info.deviceMemory;
+        bind.memoryOffset = info.offset;
+        bind.memorySize = info.size;
+
+        bind_list.push_back(bind);
+    }
+
+    if(vkBindVideoSessionMemoryKHR(device->get(), this->video_session, bind_list.size(), bind_list.data()) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    return true;
 }
 
 bool encoder::create_sampler(lava::device_ptr device)
 {
-	VkSamplerCreateInfo sampler_create_info;
-	sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
-	sampler_create_info.pNext = nullptr;
-	sampler_create_info.flags = 0;
-	sampler_create_info.magFilter = VK_FILTER_NEAREST;
-	sampler_create_info.minFilter = VK_FILTER_NEAREST;
-	sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
-	sampler_create_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
-	sampler_create_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
-	sampler_create_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
-	sampler_create_info.mipLodBias = 0.0f;
-	sampler_create_info.anisotropyEnable = VK_FALSE;
-	sampler_create_info.maxAnisotropy = 1.0f;
-	sampler_create_info.compareEnable = VK_FALSE;
-	sampler_create_info.compareOp = VK_COMPARE_OP_LESS;
-	sampler_create_info.minLod = 0;
-	sampler_create_info.maxLod = VK_LOD_CLAMP_NONE;
-	sampler_create_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
-	sampler_create_info.unnormalizedCoordinates = VK_FALSE;
-
-	if(vkCreateSampler(device->get(), &sampler_create_info, lava::memory::alloc(), &this->sampler) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	return true;
+    VkSamplerCreateInfo sampler_create_info;
+    sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+    sampler_create_info.pNext = nullptr;
+    sampler_create_info.flags = 0;
+    sampler_create_info.magFilter = VK_FILTER_NEAREST;
+    sampler_create_info.minFilter = VK_FILTER_NEAREST;
+    sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
+    sampler_create_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+    sampler_create_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+    sampler_create_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+    sampler_create_info.mipLodBias = 0.0f;
+    sampler_create_info.anisotropyEnable = VK_FALSE;
+    sampler_create_info.maxAnisotropy = 1.0f;
+    sampler_create_info.compareEnable = VK_FALSE;
+    sampler_create_info.compareOp = VK_COMPARE_OP_LESS;
+    sampler_create_info.minLod = 0;
+    sampler_create_info.maxLod = VK_LOD_CLAMP_NONE;
+    sampler_create_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
+    sampler_create_info.unnormalizedCoordinates = VK_FALSE;
+
+    if(vkCreateSampler(device->get(), &sampler_create_info, lava::memory::alloc(), &this->sampler) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    return true;
 }
 
 bool encoder::create_pools(lava::device_ptr device, uint32_t frame_count)
 {
-	VkCommandPoolCreateInfo command_create_info;
-	command_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
-	command_create_info.pNext = nullptr;
-	command_create_info.flags = 0;
-	command_create_info.queueFamilyIndex = this->encode_queue->family;
-
-	if(vkCreateCommandPool(device->get(), &command_create_info, lava::memory::alloc(), &this->command_pool) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	VkQueryPoolCreateInfo query_create_info;
-	query_create_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
-	query_create_info.pNext = nullptr;
-	query_create_info.flags = 0;
-	query_create_info.queryType = VK_QUERY_TYPE_VIDEO_ENCODE_BITSTREAM_BUFFER_RANGE_KHR;
-	query_create_info.queryCount = frame_count;
-	query_create_info.pipelineStatistics = 0;
-
-	//TODO: Fix error
-	uint32_t result = vkCreateQueryPool(device->get(), &query_create_info, lava::memory::alloc(), &this->query_pool);
-
-	if(result == VK_ERROR_OUT_OF_DEVICE_MEMORY)
-	{
-		return false;
-	}
-
-	if(result == VK_ERROR_OUT_OF_HOST_MEMORY)
-	{
-		return false;
-	}
-
-	if(result == VK_ERROR_INITIALIZATION_FAILED)
-	{
-		return false;
-	}
-
-	if(result != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	this->descriptor_pool = lava::make_descriptor_pool();
-	
-	if(!this->descriptor_pool->create(device, {{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, frame_count}}, frame_count))
-	{
-		return false;
-	}
-
-	return true;
+    VkCommandPoolCreateInfo command_create_info;
+    command_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+    command_create_info.pNext = nullptr;
+    command_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+    command_create_info.queueFamilyIndex = this->encode_queue->family;
+
+    if(vkCreateCommandPool(device->get(), &command_create_info, lava::memory::alloc(), &this->command_pool) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    VkQueryPoolCreateInfo query_create_info;
+    query_create_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
+    query_create_info.pNext = &this->video_profile;
+    query_create_info.flags = 0;
+    query_create_info.queryType = VK_QUERY_TYPE_VIDEO_ENCODE_BITSTREAM_BUFFER_RANGE_KHR;
+    query_create_info.queryCount = frame_count;
+    query_create_info.pipelineStatistics = 0;
+
+    if(vkCreateQueryPool(device->get(), &query_create_info, lava::memory::alloc(), &this->query_pool) != VK_SUCCESS)
+    {
+        return false;    
+    }
+
+    this->descriptor_pool = lava::make_descriptor_pool();
+    
+    if(!this->descriptor_pool->create(device, {{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, frame_count}}, frame_count))
+    {
+        return false;
+    }
+
+    return true;
 }
 
 bool encoder::create_layouts(lava::device_ptr device)
 {
-	this->descriptor_layout = lava::make_descriptor();
-	this->descriptor_layout->add_binding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT);
+    this->descriptor_layout = lava::make_descriptor();
+    this->descriptor_layout->add_binding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT);
 
-	if(!this->descriptor_layout->create(device))
-	{
-		return false;
-	}
+    if(!this->descriptor_layout->create(device))
+    {
+        return false;
+    }
 
-	this->pipeline_layout = lava::make_pipeline_layout();
-	this->pipeline_layout->add_descriptor(this->descriptor_layout);
+    this->pipeline_layout = lava::make_pipeline_layout();
+    this->pipeline_layout->add_descriptor(this->descriptor_layout);
 
-	if(!this->pipeline_layout->create(device))
-	{
-		return false;
-	}
+    if(!this->pipeline_layout->create(device))
+    {
+        return false;
+    }
 
-	return true;
+    return true;
 }
 
 bool encoder::create_input_images(lava::device_ptr device, encoder_frame::ptr frame, const glm::uvec2& size)
 {
-	VkExtent3D image_extend;
-	image_extend.width = size.x;
-	image_extend.height = size.y;
-	image_extend.depth = 1;
-
-	VkImageCreateInfo luma_create_info;
-	luma_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
-	luma_create_info.pNext = nullptr;
-	luma_create_info.flags = VK_IMAGE_CREATE_ALIAS_BIT; //Allow the luma image and combined image to share the same device memory
-	luma_create_info.imageType = VK_IMAGE_TYPE_2D;
-	luma_create_info.format = VK_FORMAT_R8_UNORM;
-	luma_create_info.extent = image_extend;
-	luma_create_info.mipLevels = 1;
-	luma_create_info.arrayLayers = 1;
-	luma_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
-	luma_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
-	luma_create_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
-	luma_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
-	luma_create_info.queueFamilyIndexCount = 0;
-	luma_create_info.pQueueFamilyIndices = nullptr; //Use explicit ownership transfers
-	luma_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-
-	if(vkCreateImage(device->get(), &luma_create_info, lava::memory::alloc(), &frame->input_luma) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	VkImageCreateInfo chroma_create_info;
-	chroma_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
-	chroma_create_info.pNext = nullptr;
-	chroma_create_info.flags = VK_IMAGE_CREATE_ALIAS_BIT; //Allow the chroma image and combined image to share the same device memory
-	chroma_create_info.imageType = VK_IMAGE_TYPE_2D;
-	chroma_create_info.format = VK_FORMAT_R8G8_UNORM;
-	chroma_create_info.extent = image_extend;
-	chroma_create_info.mipLevels = 1;
-	chroma_create_info.arrayLayers = 1;
-	chroma_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
-	chroma_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
-	chroma_create_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
-	chroma_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
-	chroma_create_info.queueFamilyIndexCount = 0;
-	chroma_create_info.pQueueFamilyIndices = nullptr; //Use explicit ownership transfers
-	chroma_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-
-	if(vkCreateImage(device->get(), &chroma_create_info, lava::memory::alloc(), &frame->input_chroma) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	VkImageCreateInfo combined_create_info;
-	combined_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
-	combined_create_info.pNext = nullptr;
-	combined_create_info.flags = VK_IMAGE_CREATE_ALIAS_BIT | VK_IMAGE_CREATE_DISJOINT_BIT; //Allow combined image to use the device memory of the luma and chroma image
-	combined_create_info.imageType = VK_IMAGE_TYPE_2D;
-	combined_create_info.format = VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT;
-	combined_create_info.extent = image_extend;
-	combined_create_info.mipLevels = 1;
-	combined_create_info.arrayLayers = 1;
-	combined_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
-	combined_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
-	combined_create_info.usage = VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR;
-	combined_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
-	combined_create_info.queueFamilyIndexCount = 0;
-	combined_create_info.pQueueFamilyIndices = nullptr; //Use explicit ownership transfers
-	combined_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-
-	if(vkCreateImage(device->get(), &combined_create_info, lava::memory::alloc(), &frame->input_combined) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	if(!this->create_image_memory(device, frame->input_combined, VK_IMAGE_ASPECT_PLANE_0_BIT, frame->input_memory_luma))
-	{
-		return false;
-	}
-
-	if(!this->create_image_memory(device, frame->input_combined, VK_IMAGE_ASPECT_PLANE_1_BIT, frame->input_memory_chroma))
-	{
-		return false;
-	}
-	
-	VmaAllocationInfo luma_memory;
-	VmaAllocationInfo chroma_memory;
-
-	vmaGetAllocationInfo(device->alloc(), frame->input_memory_luma, &luma_memory);
-	vmaGetAllocationInfo(device->alloc(), frame->input_memory_chroma, &chroma_memory);
-
-	std::array<VkBindImagePlaneMemoryInfo, 2> plane_info_list;
-	plane_info_list[0].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO;
-	plane_info_list[0].pNext = nullptr;
-	plane_info_list[0].planeAspect = VK_IMAGE_ASPECT_PLANE_0_BIT;
-
-	plane_info_list[1].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO;
-	plane_info_list[1].pNext = nullptr;
-	plane_info_list[1].planeAspect = VK_IMAGE_ASPECT_PLANE_1_BIT;
-
-	std::array<VkBindImageMemoryInfo, 4> bind_info_list;
-	bind_info_list[0].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
-	bind_info_list[0].pNext = &plane_info_list[0];
-	bind_info_list[0].image = frame->input_combined;
-	bind_info_list[0].memory = luma_memory.deviceMemory;
-	bind_info_list[0].memoryOffset = luma_memory.offset;
-
-	bind_info_list[1].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
-	bind_info_list[1].pNext = &plane_info_list[1];
-	bind_info_list[1].image = frame->input_combined;
-	bind_info_list[1].memory = chroma_memory.deviceMemory;
-	bind_info_list[1].memoryOffset = chroma_memory.offset;
-
-	bind_info_list[2].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
-	bind_info_list[2].pNext = nullptr;
-	bind_info_list[2].image = frame->input_luma;
-	bind_info_list[2].memory = luma_memory.deviceMemory;
-	bind_info_list[2].memoryOffset = luma_memory.offset;
-
-	bind_info_list[3].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
-	bind_info_list[3].pNext = nullptr;
-	bind_info_list[3].image = frame->input_chroma;
-	bind_info_list[3].memory = chroma_memory.deviceMemory;
-	bind_info_list[3].memoryOffset = chroma_memory.offset;
-
-	if(vkBindImageMemory2(device->get(), bind_info_list.size(), bind_info_list.data()) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	if(!this->create_image_view(device, frame->input_luma, VK_FORMAT_R8_UNORM, frame->input_view_luma))
-	{
-		return false;
-	}
-
-	if(!this->create_image_view(device, frame->input_chroma, VK_FORMAT_R8G8_UNORM, frame->input_view_chroma))
-	{
-		return false;
-	}
-
-	return true;
+    VkExtent3D image_extend;
+    image_extend.width = size.x;
+    image_extend.height = size.y;
+    image_extend.depth = 1;
+
+    VkImageCreateInfo luma_create_info;
+    luma_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+    luma_create_info.pNext = nullptr;
+    luma_create_info.flags = VK_IMAGE_CREATE_ALIAS_BIT; //Allow the luma image and combined image to share the same device memory
+    luma_create_info.imageType = VK_IMAGE_TYPE_2D;
+    luma_create_info.format = VK_FORMAT_R8_UNORM;
+    luma_create_info.extent = image_extend;
+    luma_create_info.mipLevels = 1;
+    luma_create_info.arrayLayers = 1;
+    luma_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
+    luma_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
+    luma_create_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+    luma_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    luma_create_info.queueFamilyIndexCount = 0;
+    luma_create_info.pQueueFamilyIndices = nullptr; //Use explicit ownership transfers
+    luma_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+    if(vkCreateImage(device->get(), &luma_create_info, lava::memory::alloc(), &frame->input_luma) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    VkImageCreateInfo chroma_create_info;
+    chroma_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+    chroma_create_info.pNext = nullptr;
+    chroma_create_info.flags = VK_IMAGE_CREATE_ALIAS_BIT; //Allow the chroma image and combined image to share the same device memory
+    chroma_create_info.imageType = VK_IMAGE_TYPE_2D;
+    chroma_create_info.format = VK_FORMAT_R8G8_UNORM;
+    chroma_create_info.extent = image_extend;
+    chroma_create_info.mipLevels = 1;
+    chroma_create_info.arrayLayers = 1;
+    chroma_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
+    chroma_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
+    chroma_create_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+    chroma_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    chroma_create_info.queueFamilyIndexCount = 0;
+    chroma_create_info.pQueueFamilyIndices = nullptr; //Use explicit ownership transfers
+    chroma_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+    if(vkCreateImage(device->get(), &chroma_create_info, lava::memory::alloc(), &frame->input_chroma) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    VkImageCreateInfo combined_create_info;
+    combined_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+    combined_create_info.pNext = nullptr;
+    combined_create_info.flags = VK_IMAGE_CREATE_ALIAS_BIT | VK_IMAGE_CREATE_DISJOINT_BIT; //Allow combined image to use the device memory of the luma and chroma image
+    combined_create_info.imageType = VK_IMAGE_TYPE_2D;
+    combined_create_info.format = VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT;
+    combined_create_info.extent = image_extend;
+    combined_create_info.mipLevels = 1;
+    combined_create_info.arrayLayers = 1;
+    combined_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
+    combined_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
+    combined_create_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR; //Usage sample is actualy not necessary but the validation layer wants.
+    combined_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    combined_create_info.queueFamilyIndexCount = 0;
+    combined_create_info.pQueueFamilyIndices = nullptr; //Use explicit ownership transfers
+    combined_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+    if(vkCreateImage(device->get(), &combined_create_info, lava::memory::alloc(), &frame->input_combined) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    if(!this->create_image_memory(device, frame->input_combined, VK_IMAGE_ASPECT_PLANE_0_BIT, frame->input_memory_luma))
+    {
+        return false;
+    }
+
+    if(!this->create_image_memory(device, frame->input_combined, VK_IMAGE_ASPECT_PLANE_1_BIT, frame->input_memory_chroma))
+    {
+        return false;
+    }
+    
+    VmaAllocationInfo luma_memory;
+    VmaAllocationInfo chroma_memory;
+
+    vmaGetAllocationInfo(device->alloc(), frame->input_memory_luma, &luma_memory);
+    vmaGetAllocationInfo(device->alloc(), frame->input_memory_chroma, &chroma_memory);
+
+    std::array<VkBindImagePlaneMemoryInfo, 2> plane_info_list;
+    plane_info_list[0].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO;
+    plane_info_list[0].pNext = nullptr;
+    plane_info_list[0].planeAspect = VK_IMAGE_ASPECT_PLANE_0_BIT;
+
+    plane_info_list[1].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO;
+    plane_info_list[1].pNext = nullptr;
+    plane_info_list[1].planeAspect = VK_IMAGE_ASPECT_PLANE_1_BIT;
+
+    std::array<VkBindImageMemoryInfo, 4> bind_info_list;
+    bind_info_list[0].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
+    bind_info_list[0].pNext = &plane_info_list[0];
+    bind_info_list[0].image = frame->input_combined;
+    bind_info_list[0].memory = luma_memory.deviceMemory;
+    bind_info_list[0].memoryOffset = luma_memory.offset;
+
+    bind_info_list[1].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
+    bind_info_list[1].pNext = &plane_info_list[1];
+    bind_info_list[1].image = frame->input_combined;
+    bind_info_list[1].memory = chroma_memory.deviceMemory;
+    bind_info_list[1].memoryOffset = chroma_memory.offset;
+
+    bind_info_list[2].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
+    bind_info_list[2].pNext = nullptr;
+    bind_info_list[2].image = frame->input_luma;
+    bind_info_list[2].memory = luma_memory.deviceMemory;
+    bind_info_list[2].memoryOffset = luma_memory.offset;
+
+    bind_info_list[3].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
+    bind_info_list[3].pNext = nullptr;
+    bind_info_list[3].image = frame->input_chroma;
+    bind_info_list[3].memory = chroma_memory.deviceMemory;
+    bind_info_list[3].memoryOffset = chroma_memory.offset;
+
+    if(vkBindImageMemory2(device->get(), bind_info_list.size(), bind_info_list.data()) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    if(!this->create_image_view(device, frame->input_luma, VK_FORMAT_R8_UNORM, frame->input_view_luma))
+    {
+        return false;
+    }
+
+    if(!this->create_image_view(device, frame->input_chroma, VK_FORMAT_R8G8_UNORM, frame->input_view_chroma))
+    {
+        return false;
+    }
+
+    if(!this->create_image_view(device, frame->input_combined, VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT, frame->input_view_combined))
+    {
+        return false;
+    }
+
+    return true;
 }
 
 bool encoder::create_image_memory(lava::device_ptr device, VkImage image, VkImageAspectFlagBits image_plane, VmaAllocation& memory)
 {
-	VkImagePlaneMemoryRequirementsInfo plane_info;
-	plane_info.sType = VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO;
-	plane_info.pNext = nullptr;
-	plane_info.planeAspect = image_plane;
-
-	VkImageMemoryRequirementsInfo2 memoy_info;
-	memoy_info.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2;
-	memoy_info.pNext = &plane_info;
-	memoy_info.image = image;
-
-	VkMemoryRequirements2 memory_requiremtents;
-	memory_requiremtents.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
-	memory_requiremtents.pNext = nullptr;
-
-	vkGetImageMemoryRequirements2(device->get(), &memoy_info, &memory_requiremtents);
-
-	VmaAllocationCreateInfo create_info;
-	create_info.flags = 0;
-	create_info.usage = VMA_MEMORY_USAGE_UNKNOWN;
-	create_info.requiredFlags = 0;
-	create_info.preferredFlags = 0;
-	create_info.memoryTypeBits = 0;
-	create_info.pool = nullptr;
-	create_info.pUserData = nullptr;
-	create_info.priority = 1.0f;
-
-	if(vmaAllocateMemory(device->alloc(), &memory_requiremtents.memoryRequirements, &create_info, &memory, nullptr) != VK_SUCCESS)
-	{
-		return false;
-	}
-	
-	return true;
+    VkImagePlaneMemoryRequirementsInfo plane_info;
+    plane_info.sType = VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO;
+    plane_info.pNext = nullptr;
+    plane_info.planeAspect = image_plane;
+
+    VkImageMemoryRequirementsInfo2 memoy_info;
+    memoy_info.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2;
+    memoy_info.pNext = &plane_info;
+    memoy_info.image = image;
+
+    VkMemoryRequirements2 memory_requiremtents;
+    memory_requiremtents.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
+    memory_requiremtents.pNext = nullptr;
+
+    vkGetImageMemoryRequirements2(device->get(), &memoy_info, &memory_requiremtents);
+
+    VmaAllocationCreateInfo create_info;
+    create_info.flags = 0;
+    create_info.usage = VMA_MEMORY_USAGE_UNKNOWN;
+    create_info.requiredFlags = 0;
+    create_info.preferredFlags = 0;
+    create_info.memoryTypeBits = 0;
+    create_info.pool = nullptr;
+    create_info.pUserData = nullptr;
+    create_info.priority = 1.0f;
+
+    if(vmaAllocateMemory(device->alloc(), &memory_requiremtents.memoryRequirements, &create_info, &memory, nullptr) != VK_SUCCESS)
+    {
+        return false;
+    }
+    
+    return true;
 }
 
 bool encoder::create_image_view(lava::device_ptr device, VkImage image, VkFormat image_format, VkImageView& image_view)
 {
-	VkComponentMapping components;
-	components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
-	components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
-	components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
-	components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
-
-	VkImageSubresourceRange subresource_range;
-	subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-	subresource_range.baseMipLevel = 0;
-	subresource_range.levelCount = 1;
-	subresource_range.baseArrayLayer = 0;
-	subresource_range.layerCount = 1;
-
-	VkImageViewCreateInfo create_info;
-	create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
-	create_info.pNext = nullptr;
-	create_info.flags = 0;
-	create_info.image = image;
-	create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
-	create_info.format = image_format;
-	create_info.components = components;
-	create_info.subresourceRange = subresource_range;
-
-	if(vkCreateImageView(device->get(), &create_info, lava::memory::alloc(), &image_view) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	return true;
+    VkComponentMapping components;
+    components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+    components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+    components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+    components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+
+    VkImageSubresourceRange subresource_range;
+    subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    subresource_range.baseMipLevel = 0;
+    subresource_range.levelCount = 1;
+    subresource_range.baseArrayLayer = 0;
+    subresource_range.layerCount = 1;
+
+    VkImageViewCreateInfo create_info;
+    create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+    create_info.pNext = nullptr;
+    create_info.flags = 0;
+    create_info.image = image;
+    create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+    create_info.format = image_format;
+    create_info.components = components;
+    create_info.subresourceRange = subresource_range;
+
+    if(vkCreateImageView(device->get(), &create_info, lava::memory::alloc(), &image_view) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool encoder::create_slot_image(lava::device_ptr device, encoder_frame::ptr frame, const glm::uvec2& size)
+{
+    VkExtent3D image_extend;
+    image_extend.width = size.x;
+    image_extend.height = size.y;
+    image_extend.depth = 1;
+
+    VkImageCreateInfo combined_create_info;
+    combined_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+    combined_create_info.pNext = nullptr;
+    combined_create_info.flags = VK_IMAGE_CREATE_DISJOINT_BIT;
+    combined_create_info.imageType = VK_IMAGE_TYPE_2D;
+    combined_create_info.format = VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT;
+    combined_create_info.extent = image_extend;
+    combined_create_info.mipLevels = 1;
+    combined_create_info.arrayLayers = 1;
+    combined_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
+    combined_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
+    combined_create_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR; //Usage sample is actualy not necessary but the validation layer wants.
+    combined_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    combined_create_info.queueFamilyIndexCount = 0;
+    combined_create_info.pQueueFamilyIndices = nullptr; //Use explicit ownership transfers
+    combined_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+    if(vkCreateImage(device->get(), &combined_create_info, lava::memory::alloc(), &frame->slot_image) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    if(!this->create_image_memory(device, frame->slot_image, VK_IMAGE_ASPECT_PLANE_0_BIT, frame->slot_memory_luma))
+    {
+        return false;
+    }
+
+    if(!this->create_image_memory(device, frame->slot_image, VK_IMAGE_ASPECT_PLANE_1_BIT, frame->slot_memory_chroma))
+    {
+        return false;
+    }
+
+    VmaAllocationInfo luma_memory;
+    VmaAllocationInfo chroma_memory;
+
+    vmaGetAllocationInfo(device->alloc(), frame->slot_memory_luma, &luma_memory);
+    vmaGetAllocationInfo(device->alloc(), frame->slot_memory_chroma, &chroma_memory);
+
+    std::array<VkBindImagePlaneMemoryInfo, 2> plane_info_list;
+    plane_info_list[0].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO;
+    plane_info_list[0].pNext = nullptr;
+    plane_info_list[0].planeAspect = VK_IMAGE_ASPECT_PLANE_0_BIT;
+
+    plane_info_list[1].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO;
+    plane_info_list[1].pNext = nullptr;
+    plane_info_list[1].planeAspect = VK_IMAGE_ASPECT_PLANE_1_BIT;
+
+    std::array<VkBindImageMemoryInfo, 2> bind_info_list;
+    bind_info_list[0].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
+    bind_info_list[0].pNext = &plane_info_list[0];
+    bind_info_list[0].image = frame->slot_image;
+    bind_info_list[0].memory = luma_memory.deviceMemory;
+    bind_info_list[0].memoryOffset = luma_memory.offset;
+
+    bind_info_list[1].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
+    bind_info_list[1].pNext = &plane_info_list[1];
+    bind_info_list[1].image = frame->slot_image;
+    bind_info_list[1].memory = chroma_memory.deviceMemory;
+    bind_info_list[1].memoryOffset = chroma_memory.offset;
+
+    if(vkBindImageMemory2(device->get(), bind_info_list.size(), bind_info_list.data()) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    if(!this->create_image_view(device, frame->slot_image, VK_FORMAT_R8_UNORM, frame->slot_view))
+    {
+        return false;
+    }
+
+    return true;
 }
 
 bool encoder::create_output_buffer(lava::device_ptr device, encoder_frame::ptr frame)
 {
-	if(!frame->output_buffer->create(device, nullptr, this->buffer_size, VK_BUFFER_USAGE_VIDEO_ENCODE_DST_BIT_KHR, false, VMA_MEMORY_USAGE_GPU_TO_CPU))
-	{
-		return false;
-	}
+    frame->output_buffer = lava::make_buffer();
 
-	return true;
+    if(!frame->output_buffer->create(device, nullptr, this->buffer_size, VK_BUFFER_USAGE_VIDEO_ENCODE_DST_BIT_KHR, false, VMA_MEMORY_USAGE_GPU_TO_CPU))
+    {
+        return false;
+    }
+
+    return true;
 }
 
 bool encoder::create_convert_pass(lava::device_ptr device, encoder_frame::ptr frame, const glm::uvec2& size)
 {
-	VkClearValue luma_clear_value;
-	luma_clear_value.color.float32[0] = 0.0f;
-	luma_clear_value.color.float32[1] = 0.0f;
-	luma_clear_value.color.float32[2] = 0.0f;
-	luma_clear_value.color.float32[3] = 0.0f;
-
-	VkClearValue chroma_clear_value;
-	chroma_clear_value.color.float32[0] = 0.0f;
-	chroma_clear_value.color.float32[1] = 0.0f;
-	chroma_clear_value.color.float32[2] = 0.0f;
-	chroma_clear_value.color.float32[3] = 0.0f;
-
-	lava::attachment::ptr luma_attachment = lava::make_attachment(VK_FORMAT_R8_UNORM);
-	luma_attachment->set_op(VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE);
-	luma_attachment->set_stencil_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE);
-	luma_attachment->set_layouts(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
-
-	lava::attachment::ptr chroma_attachment = lava::make_attachment(VK_FORMAT_R8G8_UNORM);
-	chroma_attachment->set_op(VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE);
-	chroma_attachment->set_stencil_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE);
-	chroma_attachment->set_layouts(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
-
-	VkAttachmentReference luma_attachment_reference;
-	luma_attachment_reference.attachment = 0;
-	luma_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-
-	VkAttachmentReference chroma_attachment_reference;
-	chroma_attachment_reference.attachment = 1;
-	chroma_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-
-	lava::subpass::ptr subpass = lava::make_subpass();
-	subpass->set_color_attachments(
-	{
-		luma_attachment_reference,
-		chroma_attachment_reference
-	});
-	
-	lava::subpass_dependency::ptr subpass_begin_dependency = lava::make_subpass_dependency(VK_SUBPASS_EXTERNAL, 0);
-	subpass_begin_dependency->set_stage_mask(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
-	subpass_begin_dependency->set_access_mask(0, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
-
-	lava::subpass_dependency::ptr subpass_end_dependency = lava::make_subpass_dependency(0, VK_SUBPASS_EXTERNAL);
-	subpass_end_dependency->set_stage_mask(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
-	subpass_end_dependency->set_access_mask(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 0);
-
-	frame->convert_pass = lava::make_render_pass(device);
-	frame->convert_pass->add(subpass);
-	frame->convert_pass->add(subpass_begin_dependency);
-	frame->convert_pass->add(subpass_end_dependency);
-	frame->convert_pass->add(luma_attachment);
-	frame->convert_pass->add(chroma_attachment);
-	frame->convert_pass->set_clear_values(
-	{
-		luma_clear_value,
-		chroma_clear_value
-	});
-
-	lava::rect framebuffer_area =
-	{
-		glm::vec2(0.0f),
-		size
-	};
-
-	lava::VkImageViews framebuffer_views =
-	{
-		frame->input_view_luma,
-		frame->input_view_chroma
-	};
-
-	if(!frame->convert_pass->create({framebuffer_views}, framebuffer_area))
-	{
-		return false;
-	}
-
-	return true;
+    VkClearValue luma_clear_value;
+    luma_clear_value.color.float32[0] = 0.0f;
+    luma_clear_value.color.float32[1] = 0.0f;
+    luma_clear_value.color.float32[2] = 0.0f;
+    luma_clear_value.color.float32[3] = 0.0f;
+
+    VkClearValue chroma_clear_value;
+    chroma_clear_value.color.float32[0] = 0.0f;
+    chroma_clear_value.color.float32[1] = 0.0f;
+    chroma_clear_value.color.float32[2] = 0.0f;
+    chroma_clear_value.color.float32[3] = 0.0f;
+
+    lava::attachment::ptr luma_attachment = lava::make_attachment(VK_FORMAT_R8_UNORM);
+    luma_attachment->set_op(VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE);
+    luma_attachment->set_stencil_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE);
+    luma_attachment->set_layouts(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
+
+    lava::attachment::ptr chroma_attachment = lava::make_attachment(VK_FORMAT_R8G8_UNORM);
+    chroma_attachment->set_op(VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE);
+    chroma_attachment->set_stencil_op(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE);
+    chroma_attachment->set_layouts(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
+
+    VkAttachmentReference luma_attachment_reference;
+    luma_attachment_reference.attachment = 0;
+    luma_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+    VkAttachmentReference chroma_attachment_reference;
+    chroma_attachment_reference.attachment = 1;
+    chroma_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+    lava::subpass::ptr subpass = lava::make_subpass();
+    subpass->set_color_attachments(
+    {
+        luma_attachment_reference,
+        chroma_attachment_reference
+    });
+    
+    lava::subpass_dependency::ptr subpass_begin_dependency = lava::make_subpass_dependency(VK_SUBPASS_EXTERNAL, 0);
+    subpass_begin_dependency->set_stage_mask(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
+    subpass_begin_dependency->set_access_mask(0, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
+
+    lava::subpass_dependency::ptr subpass_end_dependency = lava::make_subpass_dependency(0, VK_SUBPASS_EXTERNAL);
+    subpass_end_dependency->set_stage_mask(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
+    subpass_end_dependency->set_access_mask(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 0);
+
+    frame->convert_pass = lava::make_render_pass(device);
+    frame->convert_pass->add(subpass);
+    frame->convert_pass->add(subpass_begin_dependency);
+    frame->convert_pass->add(subpass_end_dependency);
+    frame->convert_pass->add(luma_attachment);
+    frame->convert_pass->add(chroma_attachment);
+    frame->convert_pass->set_clear_values(
+    {
+        luma_clear_value,
+        chroma_clear_value
+    });
+
+    lava::rect framebuffer_area =
+    {
+        glm::vec2(0.0f),
+        size
+    };
+
+    lava::VkImageViews framebuffer_views =
+    {
+        frame->input_view_luma,
+        frame->input_view_chroma
+    };
+
+    if(!frame->convert_pass->create({framebuffer_views}, framebuffer_area))
+    {
+        return false;
+    }
+
+    return true;
 }
 
 bool encoder::create_convert_pipeline(lava::device_ptr device, encoder_frame::ptr frame)
 {
-	VkPipelineColorBlendAttachmentState luma_blend_state;
-	luma_blend_state.blendEnable = VK_FALSE;
-	luma_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO;
-	luma_blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
-	luma_blend_state.colorBlendOp = VK_BLEND_OP_ADD;
-	luma_blend_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
-	luma_blend_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
-	luma_blend_state.alphaBlendOp = VK_BLEND_OP_ADD;
-	luma_blend_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
-
-	VkPipelineColorBlendAttachmentState chroma_blend_state;
-	chroma_blend_state.blendEnable = VK_FALSE;
-	chroma_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO;
-	chroma_blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
-	chroma_blend_state.colorBlendOp = VK_BLEND_OP_ADD;
-	chroma_blend_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
-	chroma_blend_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
-	chroma_blend_state.alphaBlendOp = VK_BLEND_OP_ADD;
-	chroma_blend_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
-
-	frame->convert_pipeline = lava::make_graphics_pipeline(device);
-	frame->convert_pipeline->set_layout(this->pipeline_layout);
-	frame->convert_pipeline->set_input_assembly_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
-	frame->convert_pipeline->add_color_blend_attachment(luma_blend_state);
-	frame->convert_pipeline->add_color_blend_attachment(chroma_blend_state);
-
-	if(!frame->convert_pipeline->add_shader(lava::file_data("dpr/convert_rgb_to_yuv_vertex.spirv"), VK_SHADER_STAGE_VERTEX_BIT))
-	{
-		return false;
-	}
-
-	if(!frame->convert_pipeline->add_shader(lava::file_data("dpr/convert_rgb_to_yuv_fragment.spirv"), VK_SHADER_STAGE_FRAGMENT_BIT))
-	{
-		return false;
-	}
-
-	frame->convert_pipeline->on_process = [this](VkCommandBuffer command_buffer)
-	{
-		this->convert_pass(command_buffer);
-	};
-
-	if(!frame->convert_pipeline->create(frame->convert_pass->get()))
-	{
-		return false;
-	}
-
-	frame->convert_pass->add_front(frame->convert_pipeline);
-	frame->convert_descriptor = this->descriptor_layout->allocate(this->descriptor_pool->get());
-
-	return true;
+    VkPipelineColorBlendAttachmentState luma_blend_state;
+    luma_blend_state.blendEnable = VK_FALSE;
+    luma_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO;
+    luma_blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
+    luma_blend_state.colorBlendOp = VK_BLEND_OP_ADD;
+    luma_blend_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+    luma_blend_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+    luma_blend_state.alphaBlendOp = VK_BLEND_OP_ADD;
+    luma_blend_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+
+    VkPipelineColorBlendAttachmentState chroma_blend_state;
+    chroma_blend_state.blendEnable = VK_FALSE;
+    chroma_blend_state.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO;
+    chroma_blend_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
+    chroma_blend_state.colorBlendOp = VK_BLEND_OP_ADD;
+    chroma_blend_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+    chroma_blend_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+    chroma_blend_state.alphaBlendOp = VK_BLEND_OP_ADD;
+    chroma_blend_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+
+    frame->convert_pipeline = lava::make_graphics_pipeline(device);
+    frame->convert_pipeline->set_layout(this->pipeline_layout);
+    frame->convert_pipeline->set_input_assembly_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
+    frame->convert_pipeline->add_color_blend_attachment(luma_blend_state);
+    frame->convert_pipeline->add_color_blend_attachment(chroma_blend_state);
+
+    if(!frame->convert_pipeline->add_shader(lava::file_data("dpr/convert_rgb_to_yuv_vertex.spirv"), VK_SHADER_STAGE_VERTEX_BIT))
+    {
+        return false;
+    }
+
+    if(!frame->convert_pipeline->add_shader(lava::file_data("dpr/convert_rgb_to_yuv_fragment.spirv"), VK_SHADER_STAGE_FRAGMENT_BIT))
+    {
+        return false;
+    }
+
+    frame->convert_pipeline->on_process = [this](VkCommandBuffer command_buffer)
+    {
+        this->convert_pass(command_buffer);
+    };
+
+    if(!frame->convert_pipeline->create(frame->convert_pass->get()))
+    {
+        return false;
+    }
+
+    frame->convert_pass->add_front(frame->convert_pipeline);
+    frame->convert_descriptor = this->descriptor_layout->allocate(this->descriptor_pool->get());
+
+    return true;
 }
 
-bool encoder::create_encode_resources(lava::device_ptr device, encoder_frame::ptr frame)
+bool encoder::create_encode_resources(lava::device_ptr device, encoder_frame::ptr frame, const glm::uvec2& size)
 {
-	VkSemaphoreCreateInfo semaphore_create_info;
-	semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
-	semaphore_create_info.pNext = nullptr;
-	semaphore_create_info.flags = 0;
-
-	if(vkCreateSemaphore(device->get(), &semaphore_create_info, lava::memory::alloc(), &frame->encode_semaphore) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	VkFenceCreateInfo fence_create_info;
-	fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
-	fence_create_info.pNext = nullptr;
-	fence_create_info.flags = 0; // unsignaled
-
-	if(vkCreateFence(device->get(), &fence_create_info, lava::memory::alloc(), &frame->encode_fence) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	VkCommandBufferAllocateInfo buffer_allocate_info;
-	buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
-	buffer_allocate_info.pNext = nullptr;
-	buffer_allocate_info.commandPool = this->command_pool;
-	buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
-	buffer_allocate_info.commandBufferCount = 1;
-
-	if(vkAllocateCommandBuffers(device->get(), &buffer_allocate_info, &frame->encode_command_buffer) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	VkCommandBufferBeginInfo buffer_begin_info;
-	buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
-	buffer_begin_info.pNext = nullptr;
-	buffer_begin_info.flags = 0; //The command buffer cannot be submitted multiple times without waiting first for the completion of the command buffer
-	buffer_begin_info.pInheritanceInfo = nullptr;
-
-	if(vkBeginCommandBuffer(frame->encode_command_buffer, &buffer_begin_info) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	this->encode_pass(frame->encode_command_buffer, frame);
-
-	if(vkEndCommandBuffer(frame->encode_command_buffer) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	return true;
+    VkSemaphoreCreateInfo semaphore_create_info;
+    semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+    semaphore_create_info.pNext = nullptr;
+    semaphore_create_info.flags = 0;
+
+    if(vkCreateSemaphore(device->get(), &semaphore_create_info, lava::memory::alloc(), &frame->encode_semaphore) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    VkFenceCreateInfo fence_create_info;
+    fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+    fence_create_info.pNext = nullptr;
+    fence_create_info.flags = 0; // unsignaled
+
+    if(vkCreateFence(device->get(), &fence_create_info, lava::memory::alloc(), &frame->encode_fence) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    VkCommandBufferAllocateInfo buffer_allocate_info;
+    buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+    buffer_allocate_info.pNext = nullptr;
+    buffer_allocate_info.commandPool = this->command_pool;
+    buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+    buffer_allocate_info.commandBufferCount = 1;
+
+    if(vkAllocateCommandBuffers(device->get(), &buffer_allocate_info, &frame->encode_command_buffer) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    return true;
 }
 
 bool encoder::check_encode_support(lava::device_ptr device, const lava::queue& queue) const
 {
-	uint32_t property_count = 0;
-	vkGetPhysicalDeviceQueueFamilyProperties2(device->get_physical_device()->get(), &property_count, nullptr);
-	
-	std::vector<VkVideoQueueFamilyProperties2KHR> video_property_list;
-	video_property_list.resize(property_count);
-	std::vector<VkQueueFamilyProperties2> property_list;
-	property_list.resize(property_count);
-
-	for(uint32_t index = 0; index < property_count; index++)
-	{
-		video_property_list[index].sType = VK_STRUCTURE_TYPE_VIDEO_QUEUE_FAMILY_PROPERTIES_2_KHR;
-		video_property_list[index].pNext = nullptr;
-		property_list[index].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2;
-		property_list[index].pNext = &video_property_list[index];
-	}
-
-	vkGetPhysicalDeviceQueueFamilyProperties2(device->get_physical_device()->get(), &property_count, property_list.data());
-
-	if((video_property_list[queue.family].videoCodecOperations & VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_EXT) != 0)
-	{
-		return true;
-	}
-
-	return false;
+    uint32_t property_count = 0;
+    vkGetPhysicalDeviceQueueFamilyProperties2(device->get_physical_device()->get(), &property_count, nullptr);
+    
+    std::vector<VkVideoQueueFamilyProperties2KHR> video_property_list;
+    video_property_list.resize(property_count);
+    std::vector<VkQueueFamilyProperties2> property_list;
+    property_list.resize(property_count);
+
+    for(uint32_t index = 0; index < property_count; index++)
+    {
+        video_property_list[index].sType = VK_STRUCTURE_TYPE_VIDEO_QUEUE_FAMILY_PROPERTIES_2_KHR;
+        video_property_list[index].pNext = nullptr;
+        property_list[index].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2;
+        property_list[index].pNext = &video_property_list[index];
+    }
+
+    vkGetPhysicalDeviceQueueFamilyProperties2(device->get_physical_device()->get(), &property_count, property_list.data());
+
+    if((video_property_list[queue.family].videoCodecOperations & VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_EXT) != 0)
+    {
+        return true;
+    }
+
+    return false;
 }
 
 bool encoder::check_format_support(lava::device_ptr device, VkImageUsageFlags usage, VkFormat format) const
 {
-	VkVideoProfilesKHR video_profiles;
-	video_profiles.sType = VK_STRUCTURE_TYPE_VIDEO_PROFILES_KHR;
-	video_profiles.pNext = nullptr;
-	video_profiles.profileCount = 1;
-	video_profiles.pProfiles = &this->video_profile;
-
-	VkPhysicalDeviceVideoFormatInfoKHR video_format_info;
-	video_format_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_FORMAT_INFO_KHR;
-	video_format_info.pNext = nullptr;
-	video_format_info.imageUsage = usage;
-	video_format_info.pVideoProfiles = &video_profiles;
-
-	uint32_t format_count = 0;
-
-	if(vkGetPhysicalDeviceVideoFormatPropertiesKHR(device->get_physical_device()->get(), &video_format_info, &format_count, nullptr) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	std::vector<VkVideoFormatPropertiesKHR> format_list;
-	format_list.resize(format_count);
-
-	for(VkVideoFormatPropertiesKHR& format_properties : format_list)
-	{
-		format_properties.sType = VK_STRUCTURE_TYPE_VIDEO_FORMAT_PROPERTIES_KHR;
-		format_properties.pNext = nullptr;
-	}
-
-	if(vkGetPhysicalDeviceVideoFormatPropertiesKHR(device->get_physical_device()->get(), &video_format_info, &format_count, format_list.data()) != VK_SUCCESS)
-	{
-		return false;
-	}
-
-	for(const VkVideoFormatPropertiesKHR& format_properties : format_list)
-	{
-		if(format_properties.format == format)
-		{
-			return true;
-		}
-	}
-
-	return false;
+    VkVideoProfilesKHR video_profiles;
+    video_profiles.sType = VK_STRUCTURE_TYPE_VIDEO_PROFILES_KHR;
+    video_profiles.pNext = nullptr;
+    video_profiles.profileCount = 1;
+    video_profiles.pProfiles = &this->video_profile;
+
+    VkPhysicalDeviceVideoFormatInfoKHR video_format_info;
+    video_format_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_FORMAT_INFO_KHR;
+    video_format_info.pNext = nullptr;
+    video_format_info.imageUsage = usage;
+    video_format_info.pVideoProfiles = &video_profiles;
+
+    uint32_t format_count = 0;
+
+    if(vkGetPhysicalDeviceVideoFormatPropertiesKHR(device->get_physical_device()->get(), &video_format_info, &format_count, nullptr) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    std::vector<VkVideoFormatPropertiesKHR> format_list;
+    format_list.resize(format_count);
+
+    for(VkVideoFormatPropertiesKHR& format_properties : format_list)
+    {
+        format_properties.sType = VK_STRUCTURE_TYPE_VIDEO_FORMAT_PROPERTIES_KHR;
+        format_properties.pNext = nullptr;
+    }
+
+    if(vkGetPhysicalDeviceVideoFormatPropertiesKHR(device->get_physical_device()->get(), &video_format_info, &format_count, format_list.data()) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    for(const VkVideoFormatPropertiesKHR& format_properties : format_list)
+    {
+        if(format_properties.format == format)
+        {
+            return true;
+        }
+    }
+
+    return false;
 }
 
 void encoder::convert_pass(VkCommandBuffer command_buffer)
 {
-	vkCmdDraw(command_buffer, 4, 1, 0, 0); //Draw fullscreen quad
+    vkCmdDraw(command_buffer, 4, 1, 0, 0); //Draw fullscreen quad
+}
+
+void encoder::encode_pass_setup(VkCommandBuffer command_buffer, encoder_frame::ptr frame)
+{
+    VkImageSubresourceRange image_range;
+    image_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    image_range.baseMipLevel = 0;
+    image_range.levelCount = 1;
+    image_range.baseArrayLayer = 0;
+    image_range.layerCount = 1;
+
+    VkImageMemoryBarrier image_barrier;
+    image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+    image_barrier.pNext = nullptr;
+    image_barrier.srcAccessMask = 0;
+    image_barrier.dstAccessMask = 0;
+    image_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
+    image_barrier.newLayout = VK_IMAGE_LAYOUT_VIDEO_ENCODE_SRC_KHR;
+    image_barrier.srcQueueFamilyIndex = this->default_queue.family;
+    image_barrier.dstQueueFamilyIndex = this->encode_queue->family;
+    image_barrier.image = frame->input_combined;
+    image_barrier.subresourceRange = image_range;
+
+    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_barrier);
+
+    VkOffset2D encode_offset;
+    encode_offset.x = 0;
+    encode_offset.y = 0;
+
+    VkExtent2D encode_extend;
+    encode_extend.width = this->size.x;
+    encode_extend.height = this->size.y;
+
+    VkVideoPictureResourceKHR slot_resource;
+    slot_resource.sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_KHR;
+    slot_resource.pNext = nullptr;
+    slot_resource.codedOffset = encode_offset;
+    slot_resource.codedExtent = encode_extend;
+    slot_resource.baseArrayLayer = 0;
+    slot_resource.imageViewBinding = frame->slot_view;
+
+    VkVideoReferenceSlotKHR slot_reference;
+    slot_reference.sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_KHR;
+    slot_reference.pNext = nullptr;
+    slot_reference.slotIndex = 0;
+    slot_reference.pPictureResource = &slot_resource;
+
+    VkVideoBeginCodingInfoKHR begin_info;
+    begin_info.sType = VK_STRUCTURE_TYPE_VIDEO_BEGIN_CODING_INFO_KHR;
+    begin_info.pNext = nullptr;
+    begin_info.flags = 0;
+    begin_info.codecQualityPreset = VK_VIDEO_CODING_QUALITY_PRESET_NORMAL_BIT_KHR;
+    begin_info.videoSession = this->video_session;
+    begin_info.videoSessionParameters = this->video_session_paremeters;
+    begin_info.referenceSlotCount = 1;
+    begin_info.pReferenceSlots = &slot_reference;
+
+    vkCmdBeginVideoCodingKHR(command_buffer, &begin_info);
+
+    vkCmdResetQueryPool(command_buffer, this->query_pool, frame->output_query_index, 1);
+    vkCmdBeginQuery(command_buffer, this->query_pool, frame->output_query_index, 0);
+
+    this->encode_pass_contol(command_buffer, frame);
+    this->encode_pass_command(command_buffer, frame);
+
+    vkCmdEndQuery(command_buffer, this->query_pool, frame->output_query_index);
+
+    VkVideoEndCodingInfoKHR end_info;
+    end_info.sType = VK_STRUCTURE_TYPE_VIDEO_END_CODING_INFO_KHR;
+    end_info.pNext = nullptr;
+    end_info.flags = 0;
+
+    vkCmdEndVideoCodingKHR(command_buffer, &end_info);
+
+    VkBufferMemoryBarrier buffer_barrier;
+    buffer_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
+    buffer_barrier.pNext = nullptr;
+    buffer_barrier.srcAccessMask = 0;
+    buffer_barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
+    buffer_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    buffer_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    buffer_barrier.buffer = frame->output_buffer->get();
+    buffer_barrier.offset = 0;
+    buffer_barrier.size = VK_WHOLE_SIZE;
+
+    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0, 0, nullptr, 1, &buffer_barrier, 0, nullptr);
+}
+
+void encoder::encode_pass_command(VkCommandBuffer command_buffer, encoder_frame::ptr frame)
+{
+    const uint8_t sequence_parameter_id = 0;
+    const uint8_t picture_parameter_id = 0;
+
+    uint32_t buffer_size = frame->output_buffer->get_size();
+    buffer_size = buffer_size - buffer_size % this->video_capabillities.minBitstreamBufferSizeAlignment;
+
+    glm::uvec2 block_size;
+    block_size.x = this->video_capabillities.videoPictureExtentGranularity.width;
+    block_size.y = this->video_capabillities.videoPictureExtentGranularity.height;
+
+    glm::uvec2 block_count = size / block_size + 1u;
+
+    VkOffset2D encode_offset;
+    encode_offset.x = 0;
+    encode_offset.y = 0;
+
+    VkExtent2D encode_extend;
+    encode_extend.width = this->size.x;
+    encode_extend.height = this->size.y;
+
+    VkVideoPictureResourceKHR input_resource;
+    input_resource.sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_KHR;
+    input_resource.pNext = nullptr;
+    input_resource.codedOffset = encode_offset;
+    input_resource.codedExtent = encode_extend;
+    input_resource.baseArrayLayer = 0;
+    input_resource.imageViewBinding = frame->input_view_combined;
+
+    VkVideoPictureResourceKHR slot_resource;
+    slot_resource.sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_KHR;
+    slot_resource.pNext = nullptr;
+    slot_resource.codedOffset = encode_offset;
+    slot_resource.codedExtent = encode_extend;
+    slot_resource.baseArrayLayer = 0;
+    slot_resource.imageViewBinding = frame->slot_view;
+
+    VkVideoReferenceSlotKHR slot_reference;
+    slot_reference.sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_KHR;
+    slot_reference.pNext = nullptr;
+    slot_reference.slotIndex = 0;
+    slot_reference.pPictureResource = &slot_resource;
+
+    //--- Slot Info ----------------------------------------------------------------
+
+    StdVideoEncodeH264PictureInfoFlags slot_flags;
+    slot_flags.idr_flag = 1;
+    slot_flags.is_reference_flag = 0;
+    slot_flags.long_term_reference_flag = 0;
+
+    StdVideoEncodeH264PictureInfo slot_parameters;
+    slot_parameters.flags = slot_flags;
+    slot_parameters.pictureType = std_video_h264_picture_type_i;
+    slot_parameters.frameNum = 0;
+    slot_parameters.pictureOrderCount = this->frame_index;
+    slot_parameters.long_term_pic_num = 0;
+    slot_parameters.long_term_frame_idx = 0;
+
+    VkVideoEncodeH264DpbSlotInfoEXT slot_info;
+    slot_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_DPB_SLOT_INFO_EXT;
+    slot_info.pNext = nullptr;
+    slot_info.slotIndex = 0;
+    slot_info.pStdPictureInfo = &slot_parameters;
+
+    //--- Slice Info ----------------------------------------------------------------
+
+    StdVideoEncodeH264RefMgmtFlags memory_flags;
+    memory_flags.ref_pic_list_modification_l0_flag = 0;
+    memory_flags.ref_pic_list_modification_l1_flag = 0;
+
+    StdVideoEncodeH264RefMemMgmtCtrlOperations memory_operations;
+    memory_operations.flags = memory_flags;
+    memory_operations.refList0ModOpCount = 0;
+    memory_operations.pRefList0ModOperations = nullptr;
+    memory_operations.refList1ModOpCount = 0;
+    memory_operations.pRefList1ModOperations = nullptr;
+    memory_operations.refPicMarkingOpCount = 0;
+    memory_operations.pRefPicMarkingOperations = nullptr;
+
+    StdVideoEncodeH264SliceHeaderFlags slice_flags;
+    slice_flags.idr_flag = 1;
+    slice_flags.is_reference_flag = 0;
+    slice_flags.num_ref_idx_active_override_flag = 0;
+    slice_flags.no_output_of_prior_pics_flag = 0;
+    slice_flags.long_term_reference_flag = 0;
+    slice_flags.adaptive_ref_pic_marking_mode_flag = 0;
+    slice_flags.no_prior_references_available_flag = 0;
+
+    StdVideoEncodeH264SliceHeader slice_parameters;
+    slice_parameters.flags = slice_flags;
+    slice_parameters.slice_type = std_video_h264_slice_type_i;
+    slice_parameters.seq_parameter_set_id = sequence_parameter_id;
+    slice_parameters.pic_parameter_set_id = picture_parameter_id;
+    slice_parameters.idr_pic_id = 0;
+    slice_parameters.num_ref_idx_l0_active_minus1 = 0;
+    slice_parameters.num_ref_idx_l1_active_minus1 = 0;
+    slice_parameters.cabac_init_idc = std_video_h264_cabac_init_idc_0;
+    slice_parameters.disable_deblocking_filter_idc = std_video_h264_disable_deblocking_filter_idc_disabled;
+    slice_parameters.slice_alpha_c0_offset_div2 = 0;
+    slice_parameters.slice_beta_offset_div2 = 0;
+    slice_parameters.pMemMgmtCtrlOperations = &memory_operations;
+
+    VkVideoEncodeH264NaluSliceEXT slice_info;
+    slice_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_NALU_SLICE_EXT;
+    slice_info.pNext = nullptr;
+    slice_info.pSliceHeaderStd = &slice_parameters;
+    slice_info.mbCount = block_count.x * block_count.y;
+    slice_info.refFinalList0EntryCount = 1;
+    slice_info.pRefFinalList0Entries = &slot_info;
+    slice_info.refFinalList1EntryCount = 1;
+    slice_info.pRefFinalList1Entries = &slot_info;
+    slice_info.precedingNaluBytes = 0;
+    slice_info.minQp = 2;
+    slice_info.maxQp = 2;
+
+    //------------------------------------------------------------------------------
+
+    VkVideoEncodeH264EmitPictureParametersEXT parameter_info;
+    parameter_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_EMIT_PICTURE_PARAMETERS_EXT;
+    parameter_info.pNext = nullptr;
+    parameter_info.spsId = sequence_parameter_id;
+    parameter_info.emitSpsEnable = VK_TRUE;
+    parameter_info.ppsIdEntryCount = 1;
+    parameter_info.ppsIdEntries = &picture_parameter_id;
+
+    VkVideoEncodeH264VclFrameInfoEXT frame_info;
+    frame_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_VCL_FRAME_INFO_EXT;
+    frame_info.pNext = nullptr;//&parameter_info;
+    frame_info.refDefaultFinalList0EntryCount = 1;
+    frame_info.pRefDefaultFinalList0Entries = &slot_info;
+    frame_info.refDefaultFinalList1EntryCount = 1;
+    frame_info.pRefDefaultFinalList1Entries = &slot_info;
+    frame_info.naluSliceEntryCount = 1;
+    frame_info.pNaluSliceEntries = &slice_info;
+    frame_info.pCurrentPictureInfo = &slot_info;
+
+    VkVideoEncodeInfoKHR encode_info;
+    encode_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_INFO_KHR;
+    encode_info.pNext = &frame_info;
+    encode_info.flags = VK_VIDEO_ENCODE_RESERVED_0_BIT_KHR;
+    encode_info.qualityLevel = 0; //Not defined
+    encode_info.codedExtent = encode_extend;
+    encode_info.dstBitstreamBuffer = frame->output_buffer->get();
+    encode_info.dstBitstreamBufferOffset = 0;
+    encode_info.dstBitstreamBufferMaxRange = buffer_size;
+    encode_info.srcPictureResource = input_resource;
+    encode_info.pSetupReferenceSlot = &slot_reference;
+    encode_info.referenceSlotCount = 0;
+    encode_info.pReferenceSlots = nullptr;
+
+    vkCmdEncodeVideoKHR(command_buffer, &encode_info);
 }
 
-void encoder::encode_pass(VkCommandBuffer command_buffer, encoder_frame::ptr frame)
+void encoder::encode_pass_contol(VkCommandBuffer command_buffer, encoder_frame::ptr frame)
 {
-	VkImageSubresourceRange image_range;
-	image_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-	image_range.baseMipLevel = 0;
-	image_range.levelCount = 1;
-	image_range.baseArrayLayer = 0;
-	image_range.layerCount = 1;
-
-	VkImageMemoryBarrier image_barrier;
-	image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
-	image_barrier.pNext = nullptr;
-	image_barrier.srcAccessMask = 0;
-	image_barrier.dstAccessMask = 0;
-	image_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
-	image_barrier.newLayout = VK_IMAGE_LAYOUT_VIDEO_ENCODE_SRC_KHR;
-	image_barrier.srcQueueFamilyIndex = this->default_queue.family;
-	image_barrier.dstQueueFamilyIndex = this->encode_queue->family;
-	image_barrier.image = frame->input_combined;
-	image_barrier.subresourceRange = image_range;
-
-	vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_barrier);
-
-	VkVideoBeginCodingInfoKHR begin_info;
-	begin_info.sType = VK_STRUCTURE_TYPE_VIDEO_BEGIN_CODING_INFO_KHR;
-	begin_info.pNext = nullptr;
-	begin_info.flags = 0;
-	begin_info.codecQualityPreset = VK_VIDEO_CODING_QUALITY_PRESET_NORMAL_BIT_KHR;
-	begin_info.videoSession = this->video_session;
-	begin_info.videoSessionParameters = this->video_session_paremeters;
-	begin_info.referenceSlotCount = 0;
-	begin_info.pReferenceSlots = nullptr; //No Reference images for now
-
-	vkCmdBeginVideoCodingKHR(command_buffer, &begin_info);
-
-	vkCmdBeginQuery(command_buffer, this->query_pool, frame->output_query_index, 0);
-
-	//TODO: Implement encoding
-	/*const uint8_t sequence_parameter_id = 0;
-	const uint8_t picture_parameter_id = 0;
-
-	VkVideoEncodeH264EmitPictureParametersEXT parameter_info;
-	parameter_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_EMIT_PICTURE_PARAMETERS_EXT;
-	parameter_info.pNext = nullptr;
-	parameter_info.spsId = sequence_parameter_id;
-	parameter_info.emitSpsEnable = VK_TRUE;
-	parameter_info.ppsIdEntryCount = 1;
-	parameter_info.ppsIdEntries = &picture_parameter_id;
-
-	VkVideoEncodeH264VclFrameInfoEXT frame_info;
-	frame_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_VCL_FRAME_INFO_EXT;
-	frame_info.pNext = &parameter_info;
-	frame_info.refDefaultFinalList0EntryCount;
-	frame_info.pRefDefaultFinalList0Entries;
-	frame_info.refDefaultFinalList1EntryCount;
-	frame_info.pRefDefaultFinalList1Entries;
-	frame_info.naluSliceEntryCount;
-	frame_info.pNaluSliceEntries;
-	frame_info.pCurrentPictureInfo;
-
-	VkVideoEncodeInfoKHR encode_info;
-	encode_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_INFO_KHR;
-	encode_info.pNext = &frame_info;
-	encode_info.flags = VK_VIDEO_ENCODE_RESERVED_0_BIT_KHR;
-	encode_info.qualityLevel = 0; //Not defined
-	encode_info.codedExtent;
-	encode_info.dstBitstreamBuffer;
-	encode_info.dstBitstreamBufferOffset;
-	encode_info.dstBitstreamBufferMaxRange;
-	encode_info.srcPictureResource;
-	encode_info.pSetupReferenceSlot;
-	encode_info.referenceSlotCount;
-	encode_info.pReferenceSlots;
-
-	vkCmdEncodeVideoKHR(command_buffer, &encode_info);*/
-
-	vkCmdEndQuery(command_buffer, this->query_pool, frame->output_query_index);
-
-	VkVideoEndCodingInfoKHR end_info;
-	end_info.sType = VK_STRUCTURE_TYPE_VIDEO_END_CODING_INFO_KHR;
-	end_info.pNext = nullptr;
-	end_info.flags = 0;
-
-	vkCmdEndVideoCodingKHR(command_buffer, &end_info);
-
-	VkBufferMemoryBarrier buffer_barrier;
-	buffer_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
-	buffer_barrier.pNext = nullptr;
-	buffer_barrier.srcAccessMask = 0;
-	buffer_barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
-	buffer_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
-	buffer_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
-	buffer_barrier.buffer = frame->output_buffer->get();
-	buffer_barrier.offset = 0;
-	buffer_barrier.size = VK_WHOLE_SIZE;
-
-	vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0, 0, nullptr, 1, &buffer_barrier, 0, nullptr);
+    VkVideoEncodeRateControlInfoKHR encode_rate_info;
+    encode_rate_info.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_RATE_CONTROL_INFO_KHR;
+    encode_rate_info.pNext = nullptr;
+    encode_rate_info.flags = VK_VIDEO_ENCODE_RATE_CONTROL_RESET_BIT_KHR;
+    encode_rate_info.rateControlMode = VK_VIDEO_ENCODE_RATE_CONTROL_MODE_NONE_BIT_KHR;
+    encode_rate_info.averageBitrate = 0;
+    encode_rate_info.peakToAverageBitrateRatio = 0;
+    encode_rate_info.frameRateNumerator = 0;
+    encode_rate_info.frameRateDenominator = 0;
+    encode_rate_info.virtualBufferSizeInMs = 0;
+
+    VkVideoCodingControlFlagsKHR flag = VK_VIDEO_CODING_CONTROL_RESET_BIT_KHR;
+
+    if(this->frame_index > 0)
+    {
+        flag = VK_VIDEO_CODING_CONTROL_DEFAULT_KHR;
+    }
+
+    VkVideoCodingControlInfoKHR control_info;
+    control_info.sType = VK_STRUCTURE_TYPE_VIDEO_CODING_CONTROL_INFO_KHR;
+    control_info.pNext = &encode_rate_info;
+    control_info.flags = flag;
+
+    vkCmdControlVideoCodingKHR(command_buffer, &control_info);
 }
\ No newline at end of file
diff --git a/src/encoder.hpp b/src/encoder.hpp
index 237850a3..63a1f698 100644
--- a/src/encoder.hpp
+++ b/src/encoder.hpp
@@ -14,131 +14,143 @@ typedef std::function<void(const uint8_t* data, uint32_t size)> encoder_callback
 struct encoder_frame
 {
 public:
-	using ptr = std::shared_ptr<encoder_frame>;
+    using ptr = std::shared_ptr<encoder_frame>;
 
 public:
-	VmaAllocation input_memory_luma;
-	VmaAllocation input_memory_chroma;
+    VmaAllocation input_memory_luma;
+    VmaAllocation input_memory_chroma;
 
-	VkImage input_luma = nullptr;
-	VkImage input_chroma = nullptr;
-	VkImage input_combined = nullptr;
+    VkImage input_luma = nullptr;
+    VkImage input_chroma = nullptr;
+    VkImage input_combined = nullptr;
 
-	VkImageView input_view_luma = nullptr;
-	VkImageView input_view_chroma = nullptr;
+    VkImageView input_view_luma = nullptr;
+    VkImageView input_view_chroma = nullptr;
+    VkImageView input_view_combined = nullptr;
 
-	uint32_t output_query_index = 0;
-	lava::buffer::ptr output_buffer; //TODO: create the output buffer
+    VmaAllocation slot_memory_luma;
+    VmaAllocation slot_memory_chroma;
 
-	VkDescriptorSet convert_descriptor = nullptr;
-	lava::graphics_pipeline::ptr convert_pipeline;
-	lava::render_pass::ptr convert_pass;
+    VkImage slot_image = nullptr;
+    VkImageView slot_view = nullptr;
 
-	VkSemaphore encode_semaphore = nullptr;
-	VkFence encode_fence = nullptr;
-	VkCommandBuffer encode_command_buffer = nullptr;
+    uint32_t output_query_index = 0;
+    lava::buffer::ptr output_buffer;
+
+    VkDescriptorSet convert_descriptor = nullptr;
+    lava::graphics_pipeline::ptr convert_pipeline;
+    lava::render_pass::ptr convert_pass;
+
+    VkSemaphore encode_semaphore = nullptr;
+    VkFence encode_fence = nullptr;
+    VkCommandBuffer encode_command_buffer = nullptr;
 };
 
 class encoder_thread
 {
 public:
-	using ptr = std::shared_ptr<encoder_thread>;
+    using ptr = std::shared_ptr<encoder_thread>;
 
 public:
-	encoder_thread(lava::device_ptr device, VkQueryPool query_pool, encoder_callback callback);
-	~encoder_thread();
+    encoder_thread(lava::device_ptr device, VkQueryPool query_pool, encoder_callback callback);
+    ~encoder_thread();
 
-	static ptr start_thread(lava::device_ptr device, VkQueryPool query_pool, encoder_callback callback);
+    static ptr start_thread(lava::device_ptr device, VkQueryPool query_pool, encoder_callback callback);
 
-	void push_frame(encoder_frame::ptr frame);
-	void pop_frames(std::vector<encoder_frame::ptr>& frames);
+    void push_frame(encoder_frame::ptr frame);
+    void pop_frames(std::vector<encoder_frame::ptr>& frames);
 
 private:
-	void process_frames();
+    void process_frames();
 
 private:
-	lava::device_ptr device;
-	VkQueryPool query_pool = nullptr;
-	encoder_callback callback;
+    lava::device_ptr device;
+    VkQueryPool query_pool = nullptr;
+    encoder_callback callback;
 
-	std::thread thread;
-	std::mutex mutex;
-	std::condition_variable condition;
-	std::atomic_bool running = false;
+    std::thread thread;
+    std::mutex mutex;
+    std::condition_variable condition;
+    std::atomic_bool running = false;
 
-	std::queue<encoder_frame::ptr> wait_list;
-	std::vector<encoder_frame::ptr> complete_list;
+    std::queue<encoder_frame::ptr> wait_list;
+    std::vector<encoder_frame::ptr> complete_list;
 };
 
 class encoder
 {
 public:
-	encoder() = default;
+    encoder() = default;
 
-	bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, uint32_t frame_count, const encoder_callback& callback);
-	void destroy();
+    bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, uint32_t frame_count, const encoder_callback& callback);
+    void destroy();
 
-	bool encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout);
+    bool encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout);
 
 private:
-	bool aquire_frame(encoder_frame::ptr& frame);
-	bool submit_frame(lava::device_ptr device, encoder_frame::ptr frame, lava::renderer& renderer);
-	void write_image(lava::device_ptr device, encoder_frame::ptr frame, lava::image::ptr image);
-
-	bool create_profiles(lava::device_ptr device);
-	bool create_session(lava::device_ptr device, const glm::uvec2& size);
-	bool create_parameters(lava::device_ptr device, const glm::uvec2& size);
-	bool bind_session_memory(lava::device_ptr device);
-
-	bool create_sampler(lava::device_ptr device);
-	bool create_pools(lava::device_ptr device, uint32_t frame_count);
-	bool create_layouts(lava::device_ptr device);
-
-	bool create_input_images(lava::device_ptr device, encoder_frame::ptr frame, const glm::uvec2& size);
-	bool create_image_memory(lava::device_ptr device, VkImage image, VkImageAspectFlagBits image_plane, VmaAllocation& memory);
-	bool create_image_view(lava::device_ptr device, VkImage image, VkFormat image_format, VkImageView& image_view);
-	bool create_output_buffer(lava::device_ptr device, encoder_frame::ptr frame);
-
-	bool create_convert_pass(lava::device_ptr device, encoder_frame::ptr frame, const glm::uvec2& size);
-	bool create_convert_pipeline(lava::device_ptr device, encoder_frame::ptr frame);
-	bool create_encode_resources(lava::device_ptr device, encoder_frame::ptr frame);
-
-	bool check_encode_support(lava::device_ptr device, const lava::queue& queue) const;
-	bool check_format_support(lava::device_ptr device, VkImageUsageFlags usage, VkFormat format) const;
-
-	void convert_pass(VkCommandBuffer command_buffer);
-	void encode_pass(VkCommandBuffer command_buffer, encoder_frame::ptr frame);
+    bool aquire_frame(encoder_frame::ptr& frame);
+    bool submit_frame(lava::device_ptr device, encoder_frame::ptr frame, lava::renderer& renderer);
+    void write_image(lava::device_ptr device, encoder_frame::ptr frame, lava::image::ptr image);
+    bool record_encoding(lava::device_ptr device, encoder_frame::ptr frame);
+
+    bool create_profiles(lava::device_ptr device);
+    bool create_session(lava::device_ptr device, const glm::uvec2& size);
+    bool create_parameters(lava::device_ptr device, const glm::uvec2& size);
+    bool bind_session_memory(lava::device_ptr device);
+
+    bool create_sampler(lava::device_ptr device);
+    bool create_pools(lava::device_ptr device, uint32_t frame_count);
+    bool create_layouts(lava::device_ptr device);
+
+    bool create_input_images(lava::device_ptr device, encoder_frame::ptr frame, const glm::uvec2& size);
+    bool create_image_memory(lava::device_ptr device, VkImage image, VkImageAspectFlagBits image_plane, VmaAllocation& memory);
+    bool create_image_view(lava::device_ptr device, VkImage image, VkFormat image_format, VkImageView& image_view);
+    bool create_slot_image(lava::device_ptr device, encoder_frame::ptr frame, const glm::uvec2& size);
+    bool create_output_buffer(lava::device_ptr device, encoder_frame::ptr frame);
+
+    bool create_convert_pass(lava::device_ptr device, encoder_frame::ptr frame, const glm::uvec2& size);
+    bool create_convert_pipeline(lava::device_ptr device, encoder_frame::ptr frame);
+    bool create_encode_resources(lava::device_ptr device, encoder_frame::ptr frame, const glm::uvec2& size);
+
+    bool check_encode_support(lava::device_ptr device, const lava::queue& queue) const;
+    bool check_format_support(lava::device_ptr device, VkImageUsageFlags usage, VkFormat format) const;
+
+    void convert_pass(VkCommandBuffer command_buffer);
+    void encode_pass_setup(VkCommandBuffer command_buffer, encoder_frame::ptr frame);
+    void encode_pass_command(VkCommandBuffer command_buffer, encoder_frame::ptr frame);
+    void encode_pass_contol(VkCommandBuffer command_buffer, encoder_frame::ptr frame);
 
 private:
-	glm::uvec2 size;
-	uint32_t frame_count = 0;
-	
-	encoder_callback callback;
-	encoder_thread::ptr thread;
+    glm::uvec2 size;
+    uint32_t frame_count = 0;
+    uint32_t frame_index = 0;
+    
+    encoder_callback callback;
+    encoder_thread::ptr thread;
 
-	VkSampler sampler = nullptr;
-	VkCommandPool command_pool = nullptr;
-	VkQueryPool query_pool = nullptr;
-	lava::descriptor::pool::ptr descriptor_pool;
+    VkSampler sampler = nullptr;
+    VkCommandPool command_pool = nullptr;
+    VkQueryPool query_pool = nullptr;
+    lava::descriptor::pool::ptr descriptor_pool;
 
-	lava::descriptor::ptr descriptor_layout;
-	lava::pipeline_layout::ptr pipeline_layout;
+    lava::descriptor::ptr descriptor_layout;
+    lava::pipeline_layout::ptr pipeline_layout;
 
-	std::vector<encoder_frame::ptr> frame_list;
+    std::vector<encoder_frame::ptr> frame_list;
 
-	const uint32_t buffer_size = 4194304; //4 MB
+    const uint32_t buffer_size = 4194304; //4 MB
 
 private:
-	lava::queue default_queue;
-	std::optional<lava::queue> encode_queue;
+    lava::queue default_queue;
+    std::optional<lava::queue> encode_queue;
 
-	VkVideoSessionKHR video_session = nullptr;
-	VkVideoSessionParametersKHR video_session_paremeters = nullptr;
-	std::vector<VmaAllocation> video_session_memory;
+    VkVideoSessionKHR video_session = nullptr;
+    VkVideoSessionParametersKHR video_session_paremeters = nullptr;
+    std::vector<VmaAllocation> video_session_memory;
 
-	VkVideoProfileKHR video_profile;
-	VkVideoEncodeH264ProfileEXT encode_profile;
+    VkVideoProfileKHR video_profile;
+    VkVideoEncodeH264ProfileEXT encode_profile;
 
-	VkVideoCapabilitiesKHR video_capabillities;
-	VkVideoEncodeH264CapabilitiesEXT encode_capabillities;
+    VkVideoCapabilitiesKHR video_capabillities;
+    VkVideoEncodeH264CapabilitiesEXT encode_capabillities;
 };
\ No newline at end of file
diff --git a/src/naive_stereo.cpp b/src/naive_stereo.cpp
index 625d2fd8..795517f5 100644
--- a/src/naive_stereo.cpp
+++ b/src/naive_stereo.cpp
@@ -30,13 +30,6 @@ bool naive_stereo::create() {
 }
 
 void naive_stereo::render(VkCommandBuffer command_buffer, lava::index frame) {
-    for (auto& eye_pass : render_passes_) {
-        eye_pass.render_pass->process(command_buffer, 0);
-    }
-
-    frame_capture().capture_image(command_buffer, app()->left_eye_framebuffer()->color_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, "left_eye");
-    frame_capture().capture_image(command_buffer, app()->right_eye_framebuffer()->color_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, "right_eye");
-
     pass_timer().begin_pass(command_buffer, "left_eye");
     render_passes_[0].render_pass->process(command_buffer, 0);
     pass_timer().end_pass(command_buffer);
@@ -44,6 +37,11 @@ void naive_stereo::render(VkCommandBuffer command_buffer, lava::index frame) {
     pass_timer().begin_pass(command_buffer, "right_eye");
     render_passes_[1].render_pass->process(command_buffer, 0);
     pass_timer().end_pass(command_buffer);
+
+    frame_capture().capture_image(command_buffer, app()->left_eye_framebuffer()->color_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, "left_eye");
+    frame_capture().capture_image(command_buffer, app()->right_eye_framebuffer()->color_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, "right_eye");
+
+    encoder().encode(command_buffer, renderer(), app()->left_eye_framebuffer()->color_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
 }
 
 naive_stereo::eye_render_pass naive_stereo::create_render_pass(const stereo_framebuffer::ptr& framebuffer) {
diff --git a/src/stereo_strategy.cpp b/src/stereo_strategy.cpp
index b0aece45..575fd9a7 100644
--- a/src/stereo_strategy.cpp
+++ b/src/stereo_strategy.cpp
@@ -12,3 +12,13 @@ pass_timer& stereo_strategy::pass_timer() const
 {
 	return app_->pass_timer();
 }
+
+lava::renderer& stereo_strategy::renderer() const
+{
+    return app_->renderer();
+}
+
+encoder& stereo_strategy::encoder() const
+{
+    return app_->encoder();
+}
\ No newline at end of file
diff --git a/src/stereo_strategy.hpp b/src/stereo_strategy.hpp
index 3060d29c..f3c256ff 100644
--- a/src/stereo_strategy.hpp
+++ b/src/stereo_strategy.hpp
@@ -6,6 +6,7 @@
 class vr_app;
 class frame_capture;
 class pass_timer;
+class encoder;
 
 class stereo_strategy {
 public:
@@ -19,6 +20,8 @@ public:
     inline vr_app* app() const { return app_; }
     frame_capture& frame_capture() const;
     pass_timer& pass_timer() const;
+    lava::renderer& renderer() const;
+    encoder& encoder() const;
 
 private:
     vr_app* app_;
diff --git a/src/vr_app.cpp b/src/vr_app.cpp
index 52dac2bd..ee6ee7f1 100644
--- a/src/vr_app.cpp
+++ b/src/vr_app.cpp
@@ -182,6 +182,11 @@ bool vr_app::setup() {
         return false;
     }
     
+    if(std::filesystem::exists("video.h264"))
+    {
+        std::filesystem::remove("video.h264");
+    }
+
     encoder_callback callback = [](const uint8_t* data, uint32_t size)
     {
         std::fstream file;
diff --git a/src/vr_app.hpp b/src/vr_app.hpp
index 39b2d924..ab09a229 100644
--- a/src/vr_app.hpp
+++ b/src/vr_app.hpp
@@ -23,6 +23,8 @@ public:
     auto scene() { return scene_; }
     auto& frame_capture() { return frame_capture_; }
     auto& pass_timer() { return pass_timer_; }
+    auto& renderer() { return app_->renderer; }
+    auto& encoder() { return encoder_; }
 
     auto per_frame_descriptor() const { return per_frame_descriptor_; }
 
-- 
GitLab