diff --git a/CMakeLists.txt b/CMakeLists.txt
index 939cb0d1e0fa32e82d385f0c1f5230ec56e4c04c..92ce11efb92496fafd46679b212efed5ed642a98 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -670,6 +670,7 @@ message("=======================================================================
                 ${SRC_DIR}/scene.hpp ${SRC_DIR}/scene.cpp
 				        ${SRC_DIR}/frame_capture.hpp ${SRC_DIR}/frame_capture.cpp
 				        ${SRC_DIR}/pass_timer.hpp ${SRC_DIR}/pass_timer.cpp
+						${SRC_DIR}/encoder.hpp ${SRC_DIR}/encoder.cpp
                 )
 
         target_include_directories(${NAME} PUBLIC
diff --git a/src/encoder.cpp b/src/encoder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d01458b53f68334bc6f433fb7e1abc6098f7e68a
--- /dev/null
+++ b/src/encoder.cpp
@@ -0,0 +1,340 @@
+#include "encoder.hpp"
+
+bool encoder::create(lava::device_ptr device, const glm::uvec2& size)
+{		
+	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_memory(device))
+	{
+		return false;
+	}
+
+	if(!this->create_parameters(device, size))
+	{
+		return false;
+	}
+
+	this->size = size;
+
+	return true;
+}
+
+void encoder::destroy()
+{
+
+}
+
+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;
+}
+
+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;
+}
+
+bool encoder::bind_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;
+}
+
+bool encoder::create_parameters(lava::device_ptr device, const glm::uvec2& size)
+{
+	StdVideoH264SequenceParameterSet sps_parameter;
+	sps_parameter.profile_idc;
+	sps_parameter.level_idc;
+	sps_parameter.seq_parameter_set_id;
+	sps_parameter.chroma_format_idc;
+	sps_parameter.bit_depth_luma_minus8;
+	sps_parameter.bit_depth_chroma_minus8;
+	sps_parameter.log2_max_frame_num_minus4;
+	sps_parameter.pic_order_cnt_type;
+	sps_parameter.log2_max_pic_order_cnt_lsb_minus4;
+	sps_parameter.offset_for_non_ref_pic;
+	sps_parameter.offset_for_top_to_bottom_field;
+	sps_parameter.num_ref_frames_in_pic_order_cnt_cycle;
+	sps_parameter.max_num_ref_frames;
+	sps_parameter.pic_width_in_mbs_minus1;
+	sps_parameter.pic_height_in_map_units_minus1;
+	sps_parameter.frame_crop_left_offset;
+	sps_parameter.frame_crop_right_offset;
+	sps_parameter.frame_crop_top_offset;
+	sps_parameter.frame_crop_bottom_offset;
+	sps_parameter.flags;
+	sps_parameter.offset_for_ref_frame[255]; // The number of valid values are defined by the num_ref_frames_in_pic_order_cnt_cycle
+	sps_parameter.pScalingLists;             // Must be a valid pointer if scaling_matrix_present_flag is set
+	sps_parameter.pSequenceParameterSetVui;  // Must be a valid pointer if StdVideoH264SpsFlags:vui_parameters_present_flag is set
+	
+	StdVideoH264PictureParameterSet pps_parameter;
+	pps_parameter.seq_parameter_set_id;
+	pps_parameter.pic_parameter_set_id;
+	pps_parameter.num_ref_idx_l0_default_active_minus1;
+	pps_parameter.num_ref_idx_l1_default_active_minus1;
+	pps_parameter.weighted_bipred_idc;
+	pps_parameter.pic_init_qp_minus26;
+	pps_parameter.pic_init_qs_minus26;
+	pps_parameter.chroma_qp_index_offset;
+	pps_parameter.second_chroma_qp_index_offset;
+	pps_parameter.flags;
+	pps_parameter.pScalingLists; // Must be a valid pointer if  StdVideoH264PpsFlags::scaling_matrix_present_flag is set.
+
+	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::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;
+}
+
+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;
+}
\ No newline at end of file
diff --git a/src/encoder.hpp b/src/encoder.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..17df818aa5c7b05f85cfbb94fae79cf8f950cc4c
--- /dev/null
+++ b/src/encoder.hpp
@@ -0,0 +1,37 @@
+#pragma once
+#include <liblava/lava.hpp>
+#include <optional>
+#include <vector>
+
+class encoder
+{
+public:
+	encoder() = default;
+
+	bool create(lava::device_ptr device, const glm::uvec2& size);
+	void destroy();
+
+private:
+	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_memory(lava::device_ptr device);
+
+	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;
+
+private:
+	glm::uvec2 size;
+	std::optional<lava::queue> encode_queue;
+
+	VkVideoSessionKHR video_session = nullptr;
+	VkVideoSessionParametersKHR video_session_paremeters = nullptr;
+	std::vector<VmaAllocation> video_session_memory;
+
+	VkVideoProfileKHR video_profile;
+	VkVideoEncodeH264ProfileEXT encode_profile;
+
+	VkVideoCapabilitiesKHR video_capabillities;
+	VkVideoEncodeH264CapabilitiesEXT encode_capabillities;
+};
\ No newline at end of file
diff --git a/src/vr_app.cpp b/src/vr_app.cpp
index 5e17990c52201ae298bac00b084b4ec8d21d8076..cb61aa52cee92b1681089dcc51a8021781988510 100644
--- a/src/vr_app.cpp
+++ b/src/vr_app.cpp
@@ -49,6 +49,10 @@ vr_app::vr_app(name name, argh::parser cmd_line) {
             config.param.extensions.push_back(extension.c_str());
         }
     }
+
+    //Needed in oder to get VK_KHR_get_physical_device_properties2
+    config.info.req_api_version = api_version::v1_1;
+
     app_.emplace(config);
 
     // Create device
@@ -78,6 +82,15 @@ vr_app::vr_app(name name, argh::parser cmd_line) {
             device_param.features.multiViewport = true;
             device_param.features.geometryShader = true;
             device_param.features.tessellationShader = true;
+
+            device_param.extensions.push_back("VK_KHR_video_queue");
+            device_param.extensions.push_back("VK_KHR_video_encode_queue");
+            device_param.extensions.push_back("VK_EXT_video_encode_h264");
+
+            //Dependencies of VK_KHR_video_queue and VK_KHR_video_encode_queue that are not included in Vulkan 1.1 
+            device_param.extensions.push_back("VK_KHR_synchronization2");
+
+            device_param.add_queue(VK_QUEUE_VIDEO_ENCODE_BIT_KHR);
         };
 
         // TODO: select proper device!
@@ -167,6 +180,11 @@ bool vr_app::setup() {
         return false;
     }
 
+    if(!encoder_.create(app_->device, app_->target->get_size()))
+    {
+        return false;
+    }
+
     frame_capture_.create(app_->target->get_frame_count());
     pass_timer_.create(app_->device, app_->target->get_frame_count(), 10);
 
@@ -231,6 +249,7 @@ bool vr_app::setup() {
     calculate_projection_matrices();
 
     app_->add_run_end([this]() {
+        encoder_.destroy();
         frame_capture_.destroy();
         pass_timer_.write_to_file(app_->device);
         pass_timer_.destroy(app_->device);
diff --git a/src/vr_app.hpp b/src/vr_app.hpp
index 32993277f9f16d6259c0a35e985ebb1dc4b64d1c..39b2d92417a96fa5c6e0ba11806ab8bbb8360feb 100644
--- a/src/vr_app.hpp
+++ b/src/vr_app.hpp
@@ -11,6 +11,7 @@
 #include "scene.hpp"
 #include "frame_capture.hpp"
 #include "pass_timer.hpp"
+#include "encoder.hpp"
 
 class vr_app {
 public:
@@ -70,6 +71,7 @@ private:
     ::scene::ptr scene_;
     ::frame_capture frame_capture_ = {"captures"};
     ::pass_timer pass_timer_ = {"statistics"};
+    ::encoder encoder_;
 
     bool setup_companion_window_pipeline();
     stereo_framebuffer::ptr create_framebuffer(glm::uvec2 size, vr::EVREye eye);