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;//¶meter_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 = ¶meter_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