Newer
Older
ForwardPlusRenderer / src / SwapChain.cpp
#include <stdafx.h>
#include "SwapChain.h"
#include "Buffer/DepthBuffer.h"
#include "Context.h"
#include "RenderPass.h"
#include "Scene.h"
#include "Texture.h"
#include "VulkanWindow.h"
#include "Model.h"

namespace fpr
{
SwapChain::SwapChain(fpr::Device* device, vk::SurfaceKHR& surface, std::optional<vk::UniqueSwapchainKHR> old_swapchain)
{
  if(old_swapchain != std::nullopt)
  {
    m_old_swapchain = std::move(*old_swapchain);
    is_recreation   = true;
  }
  CreateSwapchain(device, surface);
  CreateImageViews(device);
  m_depth_buffer = std::make_unique<fpr::DepthBuffer>(device, this);
  CreateRenderPasses(device);
  CreateFrameBuffers(device);

  CreateSynchronisation(device);

  m_viewport_push_constant.viewport_size_tile_size = { m_extent.width, m_extent.height, DEFAULT_TILE_SIZE };
}

vk::UniqueSwapchainKHR& SwapChain::GetUniqueHandle()
{
  return m_swapchain;
}

void SwapChain::CreateSwapchain(fpr::Device* device, vk::SurfaceKHR& surface)
{
  auto [surface_capabilities, surface_formats, present_modes] = QuerySwapChainDetails(device, surface);

  auto fifo_or_immediate = [&](const std::vector<vk::PresentModeKHR>& present_modes)
  {
    auto search_result = std::find(present_modes.begin(), present_modes.end(), vk::PresentModeKHR::eFifo);
    if(search_result != present_modes.end())
      return *search_result;
    else
      return vk::PresentModeKHR::eImmediate;
  };

  m_surface_format = QueryFirstSuitableFormat(surface_formats);
  m_present_mode   = fifo_or_immediate(present_modes);
  m_extent         = QueryExtenfpromCapabilities(surface_capabilities);
  vk::SwapchainCreateInfoKHR swap_chain_create_info{};

  // TODO: Refactor this, instead of commenting make it actually readable
  // instead
  uint32_t image_count = surface_capabilities.minImageCount + 1;

  if(surface_capabilities.maxImageCount > 0 && image_count > surface_capabilities.maxImageCount)
  {
    if(image_count < surface_capabilities.maxImageCount)
    {
      image_count++;
    }
    else
    {
      image_count = surface_capabilities.minImageCount;
    }
  }

  MAX_IMAGES_IN_FLIGHT = image_count;

  // end of confusion

  swap_chain_create_info.setSurface(surface);
  swap_chain_create_info.imageFormat      = m_surface_format.format;
  swap_chain_create_info.imageColorSpace  = m_surface_format.colorSpace;
  swap_chain_create_info.imageExtent      = m_extent;
  swap_chain_create_info.presentMode      = m_present_mode;
  swap_chain_create_info.minImageCount    = image_count;
  swap_chain_create_info.imageArrayLayers = 1;
  swap_chain_create_info.imageUsage       = vk::ImageUsageFlagBits::eColorAttachment;
  swap_chain_create_info.preTransform     = surface_capabilities.currentTransform;
  swap_chain_create_info.compositeAlpha   = vk::CompositeAlphaFlagBitsKHR::eOpaque;
  swap_chain_create_info.clipped          = VK_TRUE;

  swap_chain_create_info.oldSwapchain = is_recreation ? *m_old_swapchain : VK_NULL_HANDLE;


  uint32_t queue_family_indices[] = { device->GetGraphicsQueueIndex(), device->GetPresentQueueIndex() };

  if(queue_family_indices[0] != queue_family_indices[1])
  {
    swap_chain_create_info.imageSharingMode      = vk::SharingMode::eConcurrent;
    swap_chain_create_info.queueFamilyIndexCount = 2;
    swap_chain_create_info.pQueueFamilyIndices   = queue_family_indices;
  }
  else
  {
    swap_chain_create_info.imageSharingMode      = vk::SharingMode::eExclusive;
    swap_chain_create_info.queueFamilyIndexCount = 0;
    swap_chain_create_info.pQueueFamilyIndices   = nullptr;
  }
  auto [swapchain_result, swapchain] =
      device->GetLogicalDeviceHandle().createSwapchainKHRUnique(swap_chain_create_info);

  // todo: swapchain result
  m_swapchain = std::move(swapchain);
}

void SwapChain::CreateImageViews(fpr::Device* device)
{
  auto [images_result, swapchain_images] = device->GetLogicalDeviceHandle().getSwapchainImagesKHR(*m_swapchain);


  for(const auto& image : swapchain_images)
  {
    vk::ImageViewCreateInfo image_view_create_info{};
    image_view_create_info.setImage(image)
        .setViewType(IMAGE_VIEW_TYPE)
        .setFormat(m_surface_format.format)
        .setSubresourceRange(vk::ImageSubresourceRange()
                                 .setAspectMask(ASPECT_FLAGS)
                                 .setBaseArrayLayer(0)
                                 .setBaseMipLevel(0)
                                 .setLayerCount(1)
                                 .setLevelCount(1));

    auto [result_signal, image_view] = device->GetLogicalDeviceHandle().createImageViewUnique(image_view_create_info);
    assert(("Failed to create image view!", result_signal == vk::Result::eSuccess));

    m_swapchain_images.emplace_back(std::make_pair(image, std::move(image_view)));
  }
}

void SwapChain::SyncPresentation(fpr::Device* device, uint32_t image_index)
{
  vk::PresentInfoKHR present_info{};
  present_info.setImageIndices(image_index);
  present_info.setSwapchains(m_swapchain.get());
  present_info.setWaitSemaphores(m_render_done_semaphores[m_current_frame].get());
  [[maybe_unused]] auto present_res = device->GetGraphicsQueue().presentKHR(present_info);
  assert(present_res == vk::Result::eSuccess);
}

void SwapChain::SyncColorPass(fpr::Device* device)
{
  std::array<vk::PipelineStageFlags, 2> wait_flags = { vk::PipelineStageFlagBits::eEarlyFragmentTests,
                                                       vk::PipelineStageFlagBits::eColorAttachmentOutput };
  std::array<vk::Semaphore, 2>          wait_semaphores{ m_earlyz_semaphores[m_current_frame].get(),
                                                m_image_ready_semaphores[m_current_frame].get() };
  vk::SubmitInfo                        submit_info{};
  submit_info.setWaitSemaphores(wait_semaphores);
  submit_info.commandBufferCount = 1;
  submit_info.pCommandBuffers    = &m_command_buffers[m_current_frame].get();
  submit_info.setWaitDstStageMask(wait_flags);
  submit_info.signalSemaphoreCount = 1;
  submit_info.pSignalSemaphores    = &m_render_done_semaphores[m_current_frame].get();

  [[maybe_unused]] auto submit_res = device->GetGraphicsQueue().submit(submit_info, *m_draw_fence);
  assert(submit_res == vk::Result::eSuccess);
}

void SwapChain::CreateRenderPasses(fpr::Device* device)
{
  auto&      context      = fpr::Context::Get();
  vk::Format depth_format = m_depth_buffer->GetDepthImageFormat();

  const fpr::RenderPassOptions& color_pass_options =
      RenderPass::MakeDefaultRenderPass(m_surface_format.format, depth_format);

  const fpr::RenderPassOptions& early_z_options = RenderPass::MakeDefaultDepthPass(depth_format);
  context.m_render_graph->RenderPasses.Add("Default", std::make_unique<fpr::RenderPass>(color_pass_options, device));

  context.m_render_graph->RenderPasses.Add("EarlyZ", std::make_unique<fpr::RenderPass>(early_z_options, device));
}

void SwapChain::SyncDepthPass(fpr::Device* device)
{
  vk::SubmitInfo depth_submit_info{};
  depth_submit_info.setSignalSemaphores(m_earlyz_semaphores[m_current_frame].get())
      .setCommandBuffers(m_depth_command_buffers[m_current_frame].get());

  [[maybe_unused]] vk::Result depth_submit_res = device->GetGraphicsQueue().submit(depth_submit_info);
  assert(("Failed to sync depth pass.", depth_submit_res == vk::Result::eSuccess));
}



void SwapChain::CreateFrameBuffers(fpr::Device* device)
{
  auto& context    = fpr::Context::Get();
  auto  renderpass = context.m_render_graph->RenderPasses.Get("Default");
  auto  depth_pass = context.m_render_graph->RenderPasses.Get("EarlyZ");
  m_swapchain_framebuffers.reserve(m_swapchain_images.size());

  for(size_t i = 0; i < m_swapchain_images.size(); ++i)
  {
    std::array<vk::ImageView, 2> attachments = { m_swapchain_images[i].second.get(),
                                                 m_depth_buffer->GetDepthBufferView() };

    vk::FramebufferCreateInfo framebuffer_create_info{};

    framebuffer_create_info.renderPass      = renderpass->GetvkRenderPass();
    framebuffer_create_info.pAttachments    = attachments.data();
    framebuffer_create_info.attachmentCount = (uint32_t)attachments.size();
    framebuffer_create_info.width           = m_extent.width;
    framebuffer_create_info.height          = m_extent.height;
    framebuffer_create_info.layers          = 1;
    auto [fb_result, framebuffer] = device->GetLogicalDeviceHandle().createFramebufferUnique(framebuffer_create_info);

    assert(("Failed to create framebuffer!!", fb_result == vk::Result::eSuccess));

    m_swapchain_framebuffers.push_back(std::move(framebuffer));
  }

  std::array<vk::ImageView, 1> depth_image_view = { m_depth_buffer->GetDepthBufferView() };

  for(size_t i = 0; i < m_swapchain_images.size(); ++i)
  {
    vk::FramebufferCreateInfo framebuffer_create_info = {};
    framebuffer_create_info.renderPass                = depth_pass->GetvkRenderPass();
    framebuffer_create_info.setAttachments(depth_image_view);
    framebuffer_create_info.width  = m_extent.width;
    framebuffer_create_info.height = m_extent.height;
    framebuffer_create_info.layers = 1;

    auto [fb_result, depth_framebuffer] =
        device->GetLogicalDeviceHandle().createFramebufferUnique(framebuffer_create_info);

    assert(("Failed to create depth framebuffer!!", fb_result == vk::Result::eSuccess));
    m_depth_framebuffers.emplace_back(std::move(depth_framebuffer));
  }
}

void SwapChain::RecordCommands()
{
  RecordDepthCommands();

  RecordComputeCommands();

  RecordRenderCommands();

}

void SwapChain::CreateCommandBuffers()
{
  auto& context = fpr::Context::Get();
  // vk::commandbuffer
  vk::CommandBufferAllocateInfo command_buffer_alloc_info{};
  command_buffer_alloc_info.commandPool        = context.GetGraphicsCmdPool();
  command_buffer_alloc_info.level              = vk::CommandBufferLevel::ePrimary;
  command_buffer_alloc_info.commandBufferCount = (uint32_t)m_swapchain_framebuffers.size();


  auto [cmd_alloc_result, cmd_buffers] =
      context.GetDevice()->GetLogicalDeviceHandle().allocateCommandBuffersUnique(command_buffer_alloc_info);

  assert(("Failed to create command buffers!", cmd_alloc_result == vk::Result::eSuccess));
  m_command_buffers = std::move(cmd_buffers);

  vk::CommandBufferAllocateInfo depth_buffer_alloc{};
  depth_buffer_alloc.commandPool        = context.GetGraphicsCmdPool();
  depth_buffer_alloc.level              = vk::CommandBufferLevel::ePrimary;
  depth_buffer_alloc.commandBufferCount = (uint32_t)m_swapchain_framebuffers.size();

  auto [depth_cmd_alloc_result, depth_cmd_buffers] =
      context.GetDevice()->GetLogicalDeviceHandle().allocateCommandBuffersUnique(depth_buffer_alloc);
  assert(("Failed to create depth command buffers", depth_cmd_alloc_result == vk::Result::eSuccess));
  m_depth_command_buffers = std::move(depth_cmd_buffers);
}

void SwapChain::CreateSynchronisation(fpr::Device* device)
{
  m_image_ready_semaphores.resize(MAX_IMAGES_IN_FLIGHT);
  m_render_done_semaphores.resize(MAX_IMAGES_IN_FLIGHT);
  m_earlyz_semaphores.resize(MAX_IMAGES_IN_FLIGHT);

  vk::SemaphoreCreateInfo semaphore_create_info{};

  vk::FenceCreateInfo fence_create_info(vk::FenceCreateFlagBits::eSignaled);
  auto                create_semaphore = [&](vk::UniqueSemaphore& s)
  {
    auto [semaphore_result, semaphore] = device->GetLogicalDeviceHandle().createSemaphoreUnique(semaphore_create_info);
    assert(("Semaphore creation failed!", semaphore_result == vk::Result::eSuccess));
    s = std::move(semaphore);
  };

  auto create_fence = [&](vk::UniqueFence& f)
  {
    auto [fence_result, fence] = device->GetLogicalDeviceHandle().createFenceUnique(fence_create_info);
    assert(("Failed to create fence!", fence_result == vk::Result::eSuccess));
    f = std::move(fence);
  };

  std::for_each(m_image_ready_semaphores.begin(), m_image_ready_semaphores.end(), create_semaphore);
  std::for_each(m_render_done_semaphores.begin(), m_render_done_semaphores.end(), create_semaphore);
  std::for_each(m_earlyz_semaphores.begin(), m_earlyz_semaphores.end(), create_semaphore);
  create_fence(m_draw_fence);
}

vk::Result SwapChain::SubmitFrame()
{
  auto     device = fpr::Context::Get().GetDevice();
  uint32_t image_index;

  [[maybe_unused]] vk::Result fence_result =
      device->GetLogicalDeviceHandle().waitForFences(*m_draw_fence, false, std::numeric_limits<uint64_t>::max());

  [[maybe_unused]] vk::Result fence_reset_result = device->GetLogicalDeviceHandle().resetFences(*m_draw_fence);

  assert(("Timed out waiting for draw fence", fence_result == vk::Result::eSuccess));
  assert(("Failed to reset draw fence", fence_reset_result == vk::Result::eSuccess));

  vk::Result acquire_image_result = device->GetLogicalDeviceHandle().acquireNextImageKHR(
      *m_swapchain,
      std::numeric_limits<uint64_t>::max(),
      *m_image_ready_semaphores[m_current_frame],
      nullptr,
      &image_index);
  if(acquire_image_result != vk::Result::eSuccess)
    return acquire_image_result;


  SyncDepthPass(device);
  SyncColorPass(device);
  SyncPresentation(device, image_index);

  m_current_frame = (m_current_frame + 1) % MAX_IMAGES_IN_FLIGHT;
  return acquire_image_result;
}
SwapChain::SwapchainDetails SwapChain::QuerySwapChainDetails(fpr::Device* device, vk::SurfaceKHR& surface)
{
  auto [capabilities_result, surface_capabilities] =
      device->GetPhysicalDeviceHandle().getSurfaceCapabilitiesKHR(surface);

  // todo: assert result

  auto [formats_result, formats] = device->GetPhysicalDeviceHandle().getSurfaceFormatsKHR(surface);

  // todo: assert formats

  auto [modes_result, present_modes] = device->GetPhysicalDeviceHandle().getSurfacePresentModesKHR(surface);

  // todo: assert modes

  return std::make_tuple(surface_capabilities, formats, present_modes);
}

vk::SurfaceFormatKHR SwapChain::QueryFirstSuitableFormat(const std::vector<vk::SurfaceFormatKHR>& formats)
{
  for(const auto& available_format : formats)
  {
    for(const auto& wanted_format : wanted_formats)
    {
      if(wanted_format == available_format.format && wanted_color_space == available_format.colorSpace)
      {
        return available_format;
      }
    }
  }
  return vk::SurfaceFormatKHR{};
}

const std::vector<SwapChain::SwapChainImage>& SwapChain::GetImages()
{
  return m_swapchain_images;
}


void SwapChain::RecordRenderCommands()
{
  auto& context           = fpr::Context::Get();
  auto  render_graph      = context.m_render_graph.get();
  auto  graphics_pipeline = render_graph->GraphicsPipelines.Get("Graphics");

  for(size_t i = 0; i < m_command_buffers.size(); ++i)
  {
    vk::CommandBufferBeginInfo cmd_begin_info;
    cmd_begin_info.flags = vk::CommandBufferUsageFlagBits::eSimultaneousUse;

    [[maybe_unused]] auto cmd_begin_result = m_command_buffers[i]->begin(cmd_begin_info);

    assert(cmd_begin_result == vk::Result::eSuccess);

    vk::RenderPassBeginInfo render_pass_info = {};
    render_pass_info.renderPass              = render_graph->RenderPasses.Get("Default")->GetvkRenderPass();
    render_pass_info.framebuffer             = m_swapchain_framebuffers[i].get();
    render_pass_info.setRenderArea({ { 0, 0 }, m_extent });

    render_pass_info.setClearValues(m_clear_values);
    m_command_buffers[i]->beginRenderPass(render_pass_info, vk::SubpassContents::eInline);

    vk::Viewport viewport;
    viewport.x        = 0.0f;
    viewport.y        = static_cast<float>(m_extent.height);
    viewport.width    = static_cast<float>(m_extent.width);
    viewport.height   = -static_cast<float>(m_extent.height);
    viewport.minDepth = 0.0f;
    viewport.maxDepth = 1.0f;

    vk::Rect2D scissor{};
    scissor.setOffset({ 0, 0 });
    scissor.extent = m_extent;
    m_command_buffers[i]->setViewport(0, viewport);
    m_command_buffers[i]->setScissor(0, scissor);

    m_command_buffers[i]->bindPipeline(vk::PipelineBindPoint::eGraphics, graphics_pipeline->GetPipeline());
    m_command_buffers[i]->pushConstants<ViewportConstants>(
        graphics_pipeline->GetLayout(), vk::ShaderStageFlagBits::eFragment, 0, m_viewport_push_constant);

    fpr::Scene* scene = context.m_loaded_scene;

    auto cam_desc_set   = render_graph->DescriptorSets.Get("Camera" + std::to_string(i));
    auto light_desc_set = render_graph->DescriptorSets.Get("PointLight" + std::to_string(i));


    for(auto& model : scene->GetModels())
    {
      m_command_buffers[i]->bindIndexBuffer(model->GetIndexBuffer().GetBuffer(), 0, vk::IndexType::eUint32);

      for(auto&& mesh : model->GetMeshes())
      {
        m_command_buffers[i]->bindVertexBuffers(0, model->GetVertexBuffer().GetBuffer(), mesh.GetVertexOffset());

        std::vector<vk::DescriptorSet> desc_sets = {
          cam_desc_set,
        };

        m_command_buffers[i]->bindDescriptorSets(
            vk::PipelineBindPoint::eGraphics, graphics_pipeline->GetLayout(), 0, cam_desc_set, nullptr);

        m_command_buffers[i]->bindDescriptorSets(
            vk::PipelineBindPoint::eGraphics,
            graphics_pipeline->GetLayout(),
            1,
            render_graph->DescriptorSets.Get("Model" + std::to_string(i)),
            (uint32_t)(mesh.GetMeshIndex() * context.GetRequiredDynamicAlignment()));


        m_command_buffers[i]->bindDescriptorSets(
            vk::PipelineBindPoint::eGraphics,
            graphics_pipeline->GetLayout(),
            2,
            mesh.GetTexture()->GetDescriptorSet(),
            nullptr);

        m_command_buffers[i]->bindDescriptorSets(
            vk::PipelineBindPoint::eGraphics, graphics_pipeline->GetLayout(), 3, light_desc_set, nullptr);


        m_command_buffers[i]->drawIndexed(mesh.GetIndexCount(), 1, (uint32_t)mesh.GetIndexOffset(), 0, 0);
      }
    }
    m_command_buffers[i]->endRenderPass();

    [[maybe_unused]] auto cmd_end_result = m_command_buffers[i]->end();
    assert(cmd_end_result == vk::Result::eSuccess);
  }
}

void SwapChain::RecordDepthCommands()
{
  auto&                  context        = fpr::Context::Get();
  fpr::RenderGraph*      render_graph   = context.m_render_graph.get();
  fpr::Pipeline* depth_pipeline = render_graph->GraphicsPipelines.Get("Depth");
  for(size_t i = 0; i < m_depth_command_buffers.size(); ++i)
  {
    vk::CommandBufferBeginInfo cmd_begin_info;
    cmd_begin_info.flags                   = vk::CommandBufferUsageFlagBits::eSimultaneousUse;
    [[maybe_unused]] auto cmd_begin_result = m_depth_command_buffers[i]->begin(cmd_begin_info);

    assert(cmd_begin_result == vk::Result::eSuccess);

    vk::RenderPassBeginInfo render_pass_info = {};
    render_pass_info.renderPass              = context.m_render_graph->RenderPasses.Get("EarlyZ")->GetvkRenderPass();
    render_pass_info.framebuffer             = m_depth_framebuffers[i].get();
    render_pass_info.setRenderArea({ { 0, 0 }, m_extent });
    render_pass_info.setClearValues(m_clear_values[1]);
    m_depth_command_buffers[i]->beginRenderPass(render_pass_info, vk::SubpassContents::eInline);


    vk::Viewport viewport;
    viewport.x        = 0.0f;
    viewport.y        = static_cast<float>(m_extent.height);
    viewport.width    = static_cast<float>(m_extent.width);
    viewport.height   = -static_cast<float>(m_extent.height);
    viewport.minDepth = 0.0f;
    viewport.maxDepth = 1.0f;

    vk::Rect2D scissor{};
    scissor.setOffset({ 0, 0 });
    scissor.extent = m_extent;
    m_depth_command_buffers[i]->setViewport(0, viewport);
    m_depth_command_buffers[i]->setScissor(0, scissor);
    m_depth_command_buffers[i]->bindPipeline(vk::PipelineBindPoint::eGraphics, depth_pipeline->GetPipeline());

    auto scene = context.m_loaded_scene;

    std::array<vk::DescriptorSet, 2> desc_sets = {
      render_graph->DescriptorSets.Get("Camera" + std::to_string(i)),
      render_graph->DescriptorSets.Get("Model" + std::to_string(i)),
    };

    for(auto&& model : scene->GetModels())
    {
      m_depth_command_buffers[i]->bindIndexBuffer(model->GetIndexBuffer().GetBuffer(), 0, vk::IndexType::eUint32);
      for(auto&& mesh : model->GetMeshes())
      {
        m_depth_command_buffers[i]->bindVertexBuffers(0U, model->GetVertexBuffer().GetBuffer(), mesh.GetVertexOffset());

        m_depth_command_buffers[i]->bindDescriptorSets(
            vk::PipelineBindPoint::eGraphics,
            depth_pipeline->GetLayout(),
            0,
            render_graph->DescriptorSets.Get("Camera" + std::to_string(i)),
            nullptr);


        m_depth_command_buffers[i]->bindDescriptorSets(
            vk::PipelineBindPoint::eGraphics,
            depth_pipeline->GetLayout(),
            1,
            render_graph->DescriptorSets.Get("Model" + std::to_string(i)),
            (uint32_t)(mesh.GetMeshIndex() * context.GetRequiredDynamicAlignment()));

        m_depth_command_buffers[i]->drawIndexed(mesh.GetIndexCount(), 1, (uint32_t)mesh.GetIndexOffset(), 0, 0);
      }
    }
    m_depth_command_buffers[i]->endRenderPass();

    [[maybe_unused]] auto cmd_end_result = m_depth_command_buffers[i]->end();
    assert(cmd_end_result == vk::Result::eSuccess);
  }
}

void SwapChain::RecordComputeCommands() 
{
  auto&             context        = fpr::Context::Get();
  fpr::RenderGraph* render_graph   = context.m_render_graph.get();
  fpr::Pipeline*    compute_pipeline = render_graph->ComputePipelines.Get("LightCulling");
  for(size_t i = 0; i < m_compute_command_buffers.size(); ++i)
  {
    vk::CommandBufferBeginInfo cmd_begin_info;
    cmd_begin_info.flags                   = vk::CommandBufferUsageFlagBits::eSimultaneousUse;
    [[maybe_unused]] auto cmd_begin_result = m_compute_command_buffers[i]->begin(cmd_begin_info);

    assert(cmd_begin_result == vk::Result::eSuccess);
    //todo barriers

    std::array<vk::DescriptorSet, 3> desc_sets = {
      render_graph->DescriptorSets.Get("Camera" + std::to_string(i)),
      render_graph->DescriptorSets.Get("PointLight" + std::to_string(i)),
      render_graph->DescriptorSets.Get("DepthSampler"),
    };
    m_compute_command_buffers[i]->bindDescriptorSets(
        vk::PipelineBindPoint::eCompute, compute_pipeline->GetLayout(), 0, desc_sets, nullptr);

    m_compute_command_buffers[i]->
        pushConstants<ViewportConstants>(
            compute_pipeline->GetLayout(), vk::ShaderStageFlagBits::eCompute, 0, m_viewport_push_constant);

    glm::vec3 group_count = { m_viewport_push_constant.viewport_size_tile_size.z,
                              m_viewport_push_constant.viewport_size_tile_size.w,
                              1 };

    m_compute_command_buffers[i]->dispatch((uint32_t)group_count.x, (uint32_t)group_count.y, 1);

    [[maybe_unused]] auto cmd_end_result = m_compute_command_buffers[i]->end();
    assert(cmd_end_result == vk::Result::eSuccess);
  }
}


vk::Extent2D SwapChain::QueryExtenfpromCapabilities(vk::SurfaceCapabilitiesKHR capabilities)
{
  vk::Extent2D result{};

  if(capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max())
  {
    result = capabilities.currentExtent;
  }
  else
  {
    int width;
    int height;
    glfwGetFramebufferSize(fpr::Context::Get().GetWindow()->GetWindowHandle(), &width, &height);

    vk::Extent2D new_extent{};

    uint32_t width_constrained  = std::min(capabilities.maxImageExtent.width, static_cast<uint32_t>(width));
    uint32_t height_constrained = std::min(capabilities.maxImageExtent.height, static_cast<uint32_t>(height));

    new_extent.width  = std::max(static_cast<uint32_t>(width), width_constrained);
    new_extent.height = std::max(static_cast<uint32_t>(height), height_constrained);
    result            = new_extent;
  }
  return result;
}

const vk::Format& SwapChain::GetFormat() const FPR_NOEXCEPT
{
  return m_surface_format.format;
}

uint32_t SwapChain::GetCurrentFrameIdx() const
{
  return m_current_frame;
}

uint32_t SwapChain::GetMaxFrameIdx() const
{
  return MAX_IMAGES_IN_FLIGHT;
}

const vk::Extent2D& SwapChain::GetCurrentExtent()
{
  return m_extent;
}

fpr::DepthBuffer* SwapChain::GetDepthBuffer() FPR_NOEXCEPT
{
  return m_depth_buffer.get();
}


} // namespace fpr