56 auto f =
static_cast<VkFormat
>(format);
57 return VK_FORMAT_G8B8G8R8_422_UNORM <= f && f <= VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM;
60vk::PresentModeKHR get_present_mode(
bool vsync_enabled,
bool tearing_permitted){
61 using Mode = vk::PresentModeKHR;
63 return tearing_permitted ? Mode::eFifoRelaxed : Mode::eFifo;
65 return tearing_permitted ? Mode::eImmediate : Mode::eMailbox;
72 if (transfer_image !=
nullptr) {
73 available_img_queue.
wait_push(transfer_image);
77vk::CommandPool create_command_pool(vk::Device device, uint32_t queue_family_index) {
78 vk::CommandPoolCreateInfo pool_info{};
79 using Bits = vk::CommandPoolCreateFlagBits;
81 .setQueueFamilyIndex(queue_family_index)
82 .setFlags(Bits::eTransient | Bits::eResetCommandBuffer);
83 return device.createCommandPool(pool_info);
86std::vector<vk::CommandBuffer> create_command_buffers(vk::Device device, vk::CommandPool command_pool, uint32_t
count) {
87 vk::CommandBufferAllocateInfo allocate_info{};
89 .setCommandPool(command_pool)
90 .setLevel(vk::CommandBufferLevel::ePrimary)
91 .setCommandBufferCount(
count);
92 return device.allocateCommandBuffers(allocate_info);
95vk::Semaphore create_semaphore(vk::Device device) {
96 vk::SemaphoreCreateInfo semaphore_info{};
97 return device.createSemaphore(semaphore_info);
100vk::DescriptorPool create_descriptor_pool(vk::Device device,
size_t descriptor_count){
101 std::array<vk::DescriptorPoolSize,2> descriptor_sizes{};
103 .setType(vk::DescriptorType::eCombinedImageSampler)
104 .setDescriptorCount(descriptor_count * 2);
107 .setType(vk::DescriptorType::eStorageImage)
108 .setDescriptorCount(descriptor_count);
110 vk::DescriptorPoolCreateInfo pool_info{};
112 .setPoolSizeCount(descriptor_sizes.size())
113 .setPPoolSizes(descriptor_sizes.data())
114 .setMaxSets(descriptor_count * 3);
115 return device.createDescriptorPool(pool_info);
118std::vector<vk::DescriptorSet> allocate_description_sets(vk::Device device, vk::DescriptorPool pool, vk::DescriptorSetLayout layout,
size_t descriptor_count) {
119 std::vector<vk::DescriptorSetLayout> layouts(descriptor_count, layout);
121 vk::DescriptorSetAllocateInfo allocate_info;
123 .setDescriptorPool(pool)
124 .setDescriptorSetCount(
static_cast<uint32_t
>(layouts.size()))
125 .setPSetLayouts(layouts.data());
127 return device.allocateDescriptorSets(allocate_info);
130template<
size_t frame_count>
131void bind_conversion_images(vk::Device device, vk::Sampler sampler,
132 std::array<PerFrameResources, frame_count>& frame_resources)
134 std::vector<vk::DescriptorImageInfo> descriptor_image_infos;
135 descriptor_image_infos.reserve(frame_resources.size() * 2);
137 std::vector<vk::WriteDescriptorSet> descriptor_writes;
138 descriptor_writes.reserve(frame_resources.size() * 2);
141 vk::DescriptorImageInfo store_image_info;
143 .setImageLayout(vk::ImageLayout::eGeneral);
145 vk::WriteDescriptorSet store_write{};
148 .setDescriptorType(vk::DescriptorType::eStorageImage)
149 .setDescriptorCount(1);
151 vk::DescriptorImageInfo sample_image_info;
154 .setImageLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
156 vk::WriteDescriptorSet sample_write{};
159 .setDescriptorType(vk::DescriptorType::eCombinedImageSampler)
160 .setDescriptorCount(1);
163 for(
size_t i = 0; i < frame_resources.size(); i++){
164 auto view = frame_resources[i].converted_image.get_image_view(device,
nullptr);
165 store_image_info.imageView = view;
166 sample_image_info.imageView = view;
168 store_write.setDstSet(frame_resources[i].conversion_destination_descriptor_set);
169 sample_write.setDstSet(frame_resources[i].render_descriptor_set);
171 descriptor_image_infos.push_back(store_image_info);
172 store_write.setPImageInfo(&descriptor_image_infos.back());
173 descriptor_writes.push_back(store_write);
175 descriptor_image_infos.push_back(sample_image_info);
176 sample_write.setPImageInfo(&descriptor_image_infos.back());
177 descriptor_writes.push_back(sample_write);
179 device.updateDescriptorSets(descriptor_writes,
nullptr);
182template<
typename Fun>
187 AtScopeExit(Fun function) :
188 function{std::move(function)} { }
190 ~AtScopeExit(){ function(); };
192 AtScopeExit(
const AtScopeExit& other) =
delete;
193 AtScopeExit& operator=(
const AtScopeExit& other) =
delete;
194 AtScopeExit(AtScopeExit&& other) =
delete;
195 AtScopeExit& operator=(AtScopeExit&& other) =
delete;
199bool is_format_supported(vk::PhysicalDevice gpu,
bool is_yCbCr_supported, vk::Extent2D size, vk::Format format,
200 vk::ImageTiling tiling, vk::ImageUsageFlags usage_flags)
205 vk::ImageFormatProperties properties;
206 auto result = gpu.getImageFormatProperties(
214 case vk::Result::eSuccess:
216 case vk::Result::eErrorFormatNotSupported:
219 throw VulkanError{
"Error queriing image properties."};
222 return size.height <= properties.maxExtent.height
223 && size.width <= properties.maxExtent.width;
233 WindowChangedCallback& window, uint32_t gpu_index, std::string shaders_path,
bool vsync,
bool tearing_permitted) {
240 context.
init(std::move(
instance), surface, window_parameters, gpu_index, get_present_mode(vsync, tearing_permitted));
251 for (uint32_t i = 0; i < initial_image_count; i++) {
259 resources.image_acquired_semaphore = create_semaphore(
device);
260 resources.image_rendered_semaphore = create_semaphore(
device);
261 resources.command_buffer = command_buffers[i];
274 resorces.converted_image.destroy(
device);
290 device.destroy(resources.image_acquired_semaphore);
291 device.destroy(resources.image_rendered_semaphore);
317 vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eStorage);
321 auto image_layout = vk::ImageLayout::eShaderReadOnlyOptimal;
322 auto descriptor_type = vk::DescriptorType::eCombinedImageSampler;
334 vk::DescriptorImageInfo description_image_info;
335 description_image_info
336 .setImageLayout(image_layout)
340 vk::WriteDescriptorSet descriptor_writes{};
343 .setDescriptorType(descriptor_type)
344 .setPImageInfo(&description_image_info)
345 .setDescriptorCount(1)
346 .setDstSet(descriptor_set);
348 device.updateDescriptorSets(descriptor_writes,
nullptr);
355 cmd_buffer.reset(vk::CommandBufferResetFlags{});
357 vk::CommandBufferBeginInfo begin_info{};
358 begin_info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
359 cmd_buffer.begin(begin_info);
363 auto conversion_image_memory_barrier =
frame_resources.converted_image.create_memory_barrier(
364 vk::ImageLayout::eGeneral, vk::AccessFlagBits::eShaderWrite);
365 cmd_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eComputeShader,
366 vk::DependencyFlagBits::eByRegion,
nullptr,
nullptr, conversion_image_memory_barrier);
370 vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits::eShaderRead);
371 cmd_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eHost, vk::PipelineStageFlagBits::eComputeShader,
372 vk::DependencyFlagBits::eByRegion,
nullptr,
nullptr, transfer_image_memory_barrier);
382 vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits::eShaderRead);
384 vk::PipelineStageFlagBits::eComputeShader : vk::PipelineStageFlagBits::eHost;
385 cmd_buffer.pipelineBarrier(previous_stage, vk::PipelineStageFlagBits::eFragmentShader,
386 vk::DependencyFlagBits::eByRegion,
nullptr,
nullptr, render_begin_memory_barrier);
392 vk::ImageLayout::eGeneral, vk::AccessFlagBits::eHostWrite | vk::AccessFlagBits::eHostRead);
394 vk::PipelineStageFlagBits::eComputeShader : vk::PipelineStageFlagBits::eFragmentShader;
395 cmd_buffer.pipelineBarrier(transfer_image_last_stage, vk::PipelineStageFlagBits::eHost,
396 vk::DependencyFlagBits::eByRegion,
nullptr,
nullptr, transfer_image_memory_barrier);
405 assert(result !=
nullptr);
410 if (result !=
nullptr){
445 if (removed !=
nullptr){
454 auto& image_format_info = image_description.
format_info();
466 image_format_info.conversion_image_format : image_format_info.buffer_format);
475 auto shader_path =
path_to_shaders +
"/" + image_format_info.conversion_shader +
".comp.spv";
482 image_format_info.conversion_image_format,
483 vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eSampled,
484 vk::AccessFlagBits::eShaderWrite,
485 InitialImageData::undefined, MemoryLocation::device_local);
492 assert(conversion_source_descriptor_sets.size() ==
frame_resources.size());
493 assert(conversion_destination_descriptor_sets.size() ==
frame_resources.size());
495 resources.conversion_source_descriptor_set = conversion_source_descriptor_sets[i];
496 resources.conversion_destination_descriptor_set = conversion_destination_descriptor_sets[i];
510 if (window_parameters.is_minimized()) {
519 auto result =
device.waitForFences(first_image->is_available_fence, VK_TRUE, 0);
520 if (result == vk::Result::eSuccess){
521 device.resetFences(first_image->is_available_fence);
524 assert(first_image !=
nullptr);
527 else if (result == vk::Result::eTimeout){
542 bool frame_resources_used_by_gpu =
false;
543 AtScopeExit frame_resources_cleaner{[&](){
544 if(!frame_resources_used_by_gpu){
551 if (transfer_image_ptr ==
nullptr) {
564 int swapchain_recreation_attempt = 0;
567 swapchain_recreation_attempt++;
568 if (swapchain_recreation_attempt > 3) {
569 throw VulkanError{
"Cannot acquire swapchain image"};
573 if (window_parameters.is_minimized()) {
588 std::vector<vk::PipelineStageFlags> wait_masks{ vk::PipelineStageFlagBits::eColorAttachmentOutput };
589 vk::SubmitInfo submit_info{};
591 .setCommandBufferCount(1)
592 .setPCommandBuffers(&resources.command_buffer)
593 .setPWaitDstStageMask(wait_masks.data())
594 .setWaitSemaphoreCount(1)
595 .setPWaitSemaphores(&resources.image_acquired_semaphore)
596 .setSignalSemaphoreCount(1)
597 .setPSignalSemaphores(&resources.image_rendered_semaphore);
601 frame_resources_used_by_gpu =
true;
605 vk::PresentInfoKHR present_info{};
607 .setPImageIndices(&swapchain_image_id)
608 .setSwapchainCount(1)
609 .setPSwapchains(&swapchain)
610 .setWaitSemaphoreCount(1)
611 .setPWaitSemaphores(&resources.image_rendered_semaphore);
615 switch (present_result) {
616 case vk::Result::eSuccess:
619 case vk::Result::eErrorOutOfDateKHR:
620 case vk::Result::eSuboptimalKHR:
623 throw VulkanError{
"Error presenting image:"s + vk::to_string(present_result)};
struct video_frame * frame
static bool is_format_supported(void *data, size_t *len)
vk::Extent2D get_size() const
vulkan_display_detail::TransferImageImpl * get_transfer_image()
unsigned char * get_memory_ptr()
vk::DeviceSize get_row_pitch() const
detail::ConversionPipeline conversion_pipeline
detail::VulkanContext context
TransferImageImpl & acquire_transfer_image()
detail::RenderPipeline render_pipeline
void destroy_format_dependent_resources()
std::string path_to_shaders
void window_parameters_changed()
std::array< detail::PerFrameResources, 3 > frame_resources
vk::CommandPool command_pool
std::queue< RenderedImage > rendered_images
detail::ConcurrentQueue< TransferImageImpl * > available_img_queue
available_img_queue - producer is the render thread, consumer is the provided thread
bool queue_image(TransferImage img, bool discardable)
void copy_and_queue_image(unsigned char *frame, ImageDescription description)
vk::DescriptorPool descriptor_pool
ImageDescription current_image_description
void reconfigure(const TransferImageImpl &transfer_image)
std::vector< detail::PerFrameResources * > free_frame_resources
bool is_image_description_supported(ImageDescription description)
void init(VulkanInstance &&instance, vk::SurfaceKHR surface, uint32_t transfer_image_count, WindowChangedCallback &window, uint32_t gpu_index=no_gpu_selected, std::string path_to_shaders="./shaders", bool vsync=true, bool tearing_permitted=false)
void bind_transfer_image(TransferImageImpl &image, detail::PerFrameResources &resources)
std::vector< TransferImageImpl * > available_images
local to provider thread
bool display_queued_image()
detail::ConcurrentQueue< TransferImageImpl *, detail::filled_img_max_count > filled_img_queue
filled_img_queue - producer is the provider thread, consumer is the render thread
std::deque< TransferImageImpl > transfer_images
void record_graphics_commands(detail::PerFrameResources &commands, TransferImageImpl &transfer_image, uint32_t swapchain_image_id)
TransferImage acquire_image(ImageDescription description)
bool format_conversion_enabled
WindowChangedCallback * window
virtual WindowParameters get_window_parameters()=0
vk::SamplerYcbcrConversion get_yCbCr_conversion()
vk::Sampler get_sampler()
vk::DescriptorSetLayout get_destination_image_desc_set_layout()
void destroy(vk::Device device)
void create(vk::Device device, const std::string &shader_path, vk::Format format)
vk::DescriptorSetLayout get_source_image_desc_set_layout()
void record_commands(vk::CommandBuffer cmd_buffer, ImageSize image_size, std::array< vk::DescriptorSet, 2 > descriptor_set)
vk::ImageMemoryBarrier create_memory_barrier(vk::ImageLayout new_layout, vk::AccessFlags new_access_mask, uint32_t src_queue_family_index=VK_QUEUE_FAMILY_IGNORED, uint32_t dst_queue_family_index=VK_QUEUE_FAMILY_IGNORED)
void record_commands(vk::CommandBuffer cmd_buffer, vk::DescriptorSet image, vk::Framebuffer framebuffer)
void update_render_area(vk::Extent2D render_area_siza, vk::Extent2D image_size)
vk::DescriptorSetLayout get_image_desc_set_layout()
void create(VulkanContext &context, const std::string &path_to_shaders)
vk::SamplerYcbcrConversion get_yCbCr_conversion()
void reconfigure(vk::Device device, vk::Format sampler)
vk::RenderPass get_render_pass()
vk::Sampler get_sampler()
void destroy(vk::Device device)
void recreate(VulkanContext &context, vulkan_display::ImageDescription description)
vk::ImageView get_image_view(vk::Device device, vk::SamplerYcbcrConversion conversion)
vk::Fence is_available_fence
static constexpr uint32_t NO_ID
vulkan_display::ImageDescription get_image_description() const
vk::Framebuffer get_framebuffer(uint32_t framebuffer_id)
void recreate_swapchain(vulkan_display::WindowParameters parameters, vk::RenderPass render_pass)
bool is_yCbCr_supported() const
uint32_t get_queue_family_index()
uint32_t acquire_next_swapchain_image(vk::Semaphore acquire_semaphore) const
vk::SwapchainKHR get_swapchain()
vulkan_display::WindowParameters get_window_parameters() const
vk::Extent2D get_render_area_size()
void create_framebuffers(vk::RenderPass render_pass)
vk::PhysicalDevice get_gpu()
void init(vulkan_display::VulkanInstance &&instance, vk::SurfaceKHR surface, vulkan_display::WindowParameters window_parameters, uint32_t gpu_index, vk::PresentModeKHR preferred_mode)
constexpr bool is_yCbCr_format(vk::Format format)
std::function< void(vulkan_display::LogLevel, std::string_view)> vulkan_log_msg
vk::Extent2D get_buffer_size(const vulkan_display::ImageDescription &description)
constexpr uint32_t swapchain_image_out_of_date
const FormatInfo & format_info(vulkan_display::Format format)
constexpr uint32_t swapchain_image_timeout
const detail::FormatInfo & format_info() const
constexpr bool is_minimized() const
static ImageSize fromExtent2D(vk::Extent2D extent)
vk::DescriptorSet render_descriptor_set
vk::DescriptorSet conversion_source_descriptor_set
static class worker_pool instance