Program Listing for File Rasterizer.cpp

Program Listing for File Rasterizer.cpp#

Return to documentation for file (Src/GraphicsEngineVulkan/renderer/Rasterizer.cpp)

module;

#include <array>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <sstream>
#include <vector>
#include <vulkan/vulkan.hpp>

#include "renderer/pushConstants/PushConstantRasterizer.hpp"

#include "common/FormatHelper.hpp"

#include "common/Utilities.hpp"

module kataglyphis.vulkan.rasterizer;

import kataglyphis.vulkan.file;
import kataglyphis.vulkan.vertex;
import kataglyphis.vulkan.texture;
import kataglyphis.vulkan.image;
import kataglyphis.vulkan.scene;
import kataglyphis.vulkan.shader_helper;

namespace {
auto hasStencilComponent(vk::Format format) -> bool
{
    return format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint;
}
}// namespace

Kataglyphis::VulkanRendererInternals::Rasterizer::Rasterizer() = default;

void Kataglyphis::VulkanRendererInternals::Rasterizer::init(VulkanDevice *in_device,
  VulkanSwapChain *swap_chain,
  const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts,
  vk::CommandPool &commandPool)
{
    this->device = in_device;
    this->vulkanSwapChain = swap_chain;

    createTextures(commandPool);
    createRenderPass();
    createPushConstantRange();
    createGraphicsPipeline(descriptorSetLayouts);
    createFramebuffer();
}

void Kataglyphis::VulkanRendererInternals::Rasterizer::shaderHotReload(
  const std::vector<vk::DescriptorSetLayout> &descriptor_set_layouts)
{
    device->getLogicalDevice().destroyPipeline(graphics_pipeline);
    createGraphicsPipeline(descriptor_set_layouts);
}

auto Kataglyphis::VulkanRendererInternals::Rasterizer::getOffscreenTexture(uint32_t index) -> Kataglyphis::Texture &
{
    return *offscreenTextures[index];
}

void Kataglyphis::VulkanRendererInternals::Rasterizer::setPushConstant(PushConstantRasterizer push_constant)
{
    this->pushConstant = push_constant;
}

void Kataglyphis::VulkanRendererInternals::Rasterizer::recordCommands(vk::CommandBuffer &commandBuffer,
  uint32_t image_index,
  Scene *scene,
  const std::vector<vk::DescriptorSet> &descriptorSets)
{
    vk::RenderPassBeginInfo render_pass_begin_info;
    render_pass_begin_info.renderPass = render_pass;
    render_pass_begin_info.renderArea.offset = vk::Offset2D{ 0, 0 };
    const vk::Extent2D &swap_chain_extent = vulkanSwapChain->getSwapChainExtent();
    render_pass_begin_info.renderArea.extent = swap_chain_extent;

    std::array<vk::ClearValue, 2> clear_values = {};
    clear_values[0].color = vk::ClearColorValue{ std::array<float, 4>{ 0.2F, 0.65F, 0.4F, 1.0F } };
    clear_values[1].depthStencil = vk::ClearDepthStencilValue{ 1.0F, 0 };

    render_pass_begin_info.pClearValues = clear_values.data();
    render_pass_begin_info.clearValueCount = static_cast<uint32_t>(clear_values.size());
    render_pass_begin_info.framebuffer = framebuffer[image_index];

    commandBuffer.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline);

    commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, graphics_pipeline);

    for (uint32_t m = 0; m < scene->getModelCount(); m++) {
        pushConstant.model = scene->getModelMatrix(0);
        commandBuffer.pushConstants(
          pipeline_layout, vk::ShaderStageFlagBits::eVertex, 0, sizeof(PushConstantRasterizer), &pushConstant);

        for (unsigned int k = 0; k < scene->getMeshCount(m); k++) {
            std::vector<vk::Buffer> const vertex_buffers = { scene->getVertexBuffer(m, k) };
            vk::DeviceSize offsets[] = { 0 };
            commandBuffer.bindVertexBuffers(0, vertex_buffers, offsets);

            commandBuffer.bindIndexBuffer(scene->getIndexBuffer(m, k), 0, vk::IndexType::eUint32);

            commandBuffer.bindDescriptorSets(
              vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, descriptorSets, nullptr);

            commandBuffer.drawIndexed(scene->getIndexCount(m, k), 1, 0, 0, 0);
        }
    }

    commandBuffer.endRenderPass();
}

void Kataglyphis::VulkanRendererInternals::Rasterizer::cleanUp()
{
    for (auto &framebuffer_handle : framebuffer) { device->getLogicalDevice().destroyFramebuffer(framebuffer_handle); }

    for (const auto &texture : offscreenTextures) { texture->cleanUp(); }

    depthBufferImage->cleanUp();

    device->getLogicalDevice().destroyPipeline(graphics_pipeline);
    device->getLogicalDevice().destroyPipelineLayout(pipeline_layout);
    device->getLogicalDevice().destroyRenderPass(render_pass);
}

Kataglyphis::VulkanRendererInternals::Rasterizer::~Rasterizer() = default;

void Kataglyphis::VulkanRendererInternals::Rasterizer::createRenderPass()
{
    vk::AttachmentDescription color_attachment;
    constexpr vk::Format offscreen_format = vk::Format::eR8G8B8A8Unorm;
    color_attachment.format = offscreen_format;
    color_attachment.samples = vk::SampleCountFlagBits::e1;
    color_attachment.loadOp = vk::AttachmentLoadOp::eClear;
    color_attachment.storeOp = vk::AttachmentStoreOp::eStore;
    color_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
    color_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
    color_attachment.initialLayout = vk::ImageLayout::eUndefined;
    color_attachment.finalLayout = vk::ImageLayout::eShaderReadOnlyOptimal;

    vk::AttachmentDescription depth_attachment;
    depth_attachment.format = choose_supported_format(device->getPhysicalDevice(),
      { vk::Format::eD32SfloatS8Uint, vk::Format::eD32Sfloat, vk::Format::eD24UnormS8Uint },
      vk::ImageTiling::eOptimal,
      vk::FormatFeatureFlagBits::eDepthStencilAttachment);

    depth_attachment.samples = vk::SampleCountFlagBits::e1;
    depth_attachment.loadOp = vk::AttachmentLoadOp::eClear;
    depth_attachment.storeOp = vk::AttachmentStoreOp::eDontCare;
    depth_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
    depth_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
    depth_attachment.initialLayout = vk::ImageLayout::eUndefined;
    depth_attachment.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal;

    vk::AttachmentReference color_attachment_reference;
    color_attachment_reference.attachment = 0;
    color_attachment_reference.layout = vk::ImageLayout::eColorAttachmentOptimal;

    vk::AttachmentReference depth_attachment_reference;
    depth_attachment_reference.attachment = 1;
    depth_attachment_reference.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal;

    vk::SubpassDescription subpass;
    subpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
    subpass.colorAttachmentCount = 1;
    subpass.pColorAttachments = &color_attachment_reference;
    subpass.pDepthStencilAttachment = &depth_attachment_reference;

    std::array<vk::SubpassDependency, 1> subpass_dependencies{};

    subpass_dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
    subpass_dependencies[0].srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
    subpass_dependencies[0].srcAccessMask = vk::AccessFlags{};

    subpass_dependencies[0].dstSubpass = 0;
    subpass_dependencies[0].dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
    subpass_dependencies[0].dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
    subpass_dependencies[0].dependencyFlags = vk::DependencyFlags{};

    std::array<vk::AttachmentDescription, 2> render_pass_attachments = { color_attachment, depth_attachment };

    vk::RenderPassCreateInfo render_pass_create_info;
    render_pass_create_info.attachmentCount = static_cast<uint32_t>(render_pass_attachments.size());
    render_pass_create_info.pAttachments = render_pass_attachments.data();
    render_pass_create_info.subpassCount = 1;
    render_pass_create_info.pSubpasses = &subpass;
    render_pass_create_info.dependencyCount = static_cast<uint32_t>(subpass_dependencies.size());
    render_pass_create_info.pDependencies = subpass_dependencies.data();

    auto result = device->getLogicalDevice().createRenderPass(render_pass_create_info);
    if (result.result == vk::Result::eSuccess) {
        render_pass = result.value;
    } else {
        ASSERT_VULKAN(static_cast<VkResult>(result.result), "Failed to create render pass!")
    }
}

void Kataglyphis::VulkanRendererInternals::Rasterizer::createFramebuffer()
{
    framebuffer.resize(vulkanSwapChain->getNumberSwapChainImages());

    for (size_t i = 0; i < framebuffer.size(); i++) {
        std::array<vk::ImageView, 2> attachments = { offscreenTextures[i]->getImageView(),
            depthBufferImage->getImageView() };

        vk::FramebufferCreateInfo frame_buffer_create_info;
        frame_buffer_create_info.renderPass = render_pass;
        frame_buffer_create_info.attachmentCount = static_cast<uint32_t>(attachments.size());
        frame_buffer_create_info.pAttachments = attachments.data();
        const vk::Extent2D &swap_chain_extent = vulkanSwapChain->getSwapChainExtent();
        frame_buffer_create_info.width = swap_chain_extent.width;
        frame_buffer_create_info.height = swap_chain_extent.height;
        frame_buffer_create_info.layers = 1;

        auto result = device->getLogicalDevice().createFramebuffer(frame_buffer_create_info);
        if (result.result == vk::Result::eSuccess) {
            framebuffer[i] = result.value;
        } else {
            ASSERT_VULKAN(static_cast<VkResult>(result.result), "Failed to create framebuffer!")
        }
    }
}

void Kataglyphis::VulkanRendererInternals::Rasterizer::createPushConstantRange()
{
    push_constant_range.stageFlags = vk::ShaderStageFlagBits::eVertex;
    push_constant_range.offset = 0;
    push_constant_range.size = sizeof(PushConstantRasterizer);
}

void Kataglyphis::VulkanRendererInternals::Rasterizer::createTextures(vk::CommandPool &commandPool)
{
    offscreenTextures.resize(vulkanSwapChain->getNumberSwapChainImages());

    vk::CommandBuffer cmdBuffer = Kataglyphis::VulkanRendererInternals::CommandBufferManager::beginCommandBuffer(
      device->getLogicalDevice(), commandPool);

    for (uint32_t index = 0; index < vulkanSwapChain->getNumberSwapChainImages(); index++) {
        auto texture = std::make_unique<Texture>();
        const vk::Extent2D &swap_chain_extent = vulkanSwapChain->getSwapChainExtent();
        constexpr vk::Format offscreen_format = vk::Format::eR8G8B8A8Unorm;

        texture->createImage(device,
          swap_chain_extent.width,
          swap_chain_extent.height,
          1,
          offscreen_format,
          vk::ImageTiling::eOptimal,
          vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eStorage
            | vk::ImageUsageFlagBits::eTransferDst,
          vk::MemoryPropertyFlagBits::eDeviceLocal);

        texture->createImageView(device, offscreen_format, vk::ImageAspectFlagBits::eColor, 1);

        offscreenTextures[index] = std::move(texture);
    }

    vk::Format const depth_format = choose_supported_format(device->getPhysicalDevice(),
      { vk::Format::eD32SfloatS8Uint, vk::Format::eD32Sfloat, vk::Format::eD24UnormS8Uint },
      vk::ImageTiling::eOptimal,
      vk::FormatFeatureFlagBits::eDepthStencilAttachment);

    const vk::Extent2D &swap_chain_extent = vulkanSwapChain->getSwapChainExtent();
    depthBufferImage = std::make_unique<Texture>();
    depthBufferImage->createImage(device,
      swap_chain_extent.width,
      swap_chain_extent.height,
      1,
      depth_format,
      vk::ImageTiling::eOptimal,
      vk::ImageUsageFlagBits::eDepthStencilAttachment,
      vk::MemoryPropertyFlagBits::eDeviceLocal);

    vk::ImageAspectFlags depth_aspect_flags = vk::ImageAspectFlagBits::eDepth;
    if (hasStencilComponent(depth_format)) { depth_aspect_flags |= vk::ImageAspectFlagBits::eStencil; }

    depthBufferImage->createImageView(device, depth_format, depth_aspect_flags, 1);

    VulkanImage &vulkanImage = depthBufferImage->getVulkanImage();
    vulkanImage.transitionImageLayout(device->getLogicalDevice(),
      device->getGraphicsQueue(),
      commandPool,
      vk::ImageLayout::eUndefined,
      vk::ImageLayout::eDepthStencilAttachmentOptimal,
      depth_aspect_flags,
      1);

    Kataglyphis::VulkanRendererInternals::CommandBufferManager::endAndSubmitCommandBuffer(
      device->getLogicalDevice(), commandPool, device->getGraphicsQueue(), cmdBuffer);
}

void Kataglyphis::VulkanRendererInternals::Rasterizer::createGraphicsPipeline(
  const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts)
{
    std::stringstream rasterizer_shader_dir;
    std::filesystem::path const cwd = std::filesystem::current_path();
    rasterizer_shader_dir << cwd.string();
    rasterizer_shader_dir << RELATIVE_RESOURCE_PATH;
    rasterizer_shader_dir << "Shaders/rasterizer/";

    ShaderHelper shaderHelper;
    shaderHelper.compileShader(rasterizer_shader_dir.str(), "shader.vert");
    shaderHelper.compileShader(rasterizer_shader_dir.str(), "shader.frag");

    File vertexFile(shaderHelper.getShaderSpvDir(rasterizer_shader_dir.str(), "shader.vert"));
    File fragmentFile(shaderHelper.getShaderSpvDir(rasterizer_shader_dir.str(), "shader.frag"));
    std::vector<char> const vertex_shader_code = vertexFile.readCharSequence();
    std::vector<char> const fragment_shader_code = fragmentFile.readCharSequence();

    vk::ShaderModule vertex_shader_module = shaderHelper.createShaderModule(device, vertex_shader_code);
    vk::ShaderModule fragment_shader_module = shaderHelper.createShaderModule(device, fragment_shader_code);

    vk::PipelineShaderStageCreateInfo vertex_shader_create_info;
    vertex_shader_create_info.stage = vk::ShaderStageFlagBits::eVertex;
    vertex_shader_create_info.module = vertex_shader_module;
    vertex_shader_create_info.pName = "main";

    vk::PipelineShaderStageCreateInfo fragment_shader_create_info;
    fragment_shader_create_info.stage = vk::ShaderStageFlagBits::eFragment;
    fragment_shader_create_info.module = fragment_shader_module;
    fragment_shader_create_info.pName = "main";

    std::vector<vk::PipelineShaderStageCreateInfo> shader_stages = { vertex_shader_create_info,
        fragment_shader_create_info };

    vk::VertexInputBindingDescription binding_description;
    binding_description.binding = 0;
    binding_description.stride = sizeof(Vertex);
    binding_description.inputRate = vk::VertexInputRate::eVertex;

    std::array<vk::VertexInputAttributeDescription, 4> attribute_describtions = vertex::getVertexInputAttributeDesc();

    vk::PipelineVertexInputStateCreateInfo vertex_input_create_info;
    vertex_input_create_info.vertexBindingDescriptionCount = 1;
    vertex_input_create_info.pVertexBindingDescriptions = &binding_description;
    vertex_input_create_info.vertexAttributeDescriptionCount = static_cast<uint32_t>(attribute_describtions.size());
    vertex_input_create_info.pVertexAttributeDescriptions = attribute_describtions.data();

    vk::PipelineInputAssemblyStateCreateInfo input_assembly;
    input_assembly.topology = vk::PrimitiveTopology::eTriangleList;
    input_assembly.primitiveRestartEnable = VK_FALSE;

    vk::Viewport viewport;
    viewport.x = 0.0F;
    viewport.y = 0.0F;
    const vk::Extent2D &swap_chain_extent = vulkanSwapChain->getSwapChainExtent();
    viewport.width = static_cast<float>(swap_chain_extent.width);
    viewport.height = static_cast<float>(swap_chain_extent.height);
    viewport.minDepth = 0.0F;
    viewport.maxDepth = 1.0F;

    vk::Rect2D scissor;
    scissor.offset = vk::Offset2D{ 0, 0 };
    scissor.extent = swap_chain_extent;

    vk::PipelineViewportStateCreateInfo viewport_state_create_info;
    viewport_state_create_info.viewportCount = 1;
    viewport_state_create_info.pViewports = &viewport;
    viewport_state_create_info.scissorCount = 1;
    viewport_state_create_info.pScissors = &scissor;

    vk::PipelineRasterizationStateCreateInfo rasterizer_create_info;
    rasterizer_create_info.depthClampEnable = VK_FALSE;
    rasterizer_create_info.rasterizerDiscardEnable = VK_FALSE;
    rasterizer_create_info.polygonMode = vk::PolygonMode::eFill;
    rasterizer_create_info.lineWidth = 1.0F;
    rasterizer_create_info.cullMode = vk::CullModeFlagBits::eBack;
    rasterizer_create_info.frontFace = vk::FrontFace::eCounterClockwise;
    rasterizer_create_info.depthBiasClamp = VK_FALSE;

    vk::PipelineMultisampleStateCreateInfo multisample_create_info;
    multisample_create_info.sampleShadingEnable = VK_FALSE;
    multisample_create_info.rasterizationSamples = vk::SampleCountFlagBits::e1;

    vk::PipelineColorBlendAttachmentState color_state;
    color_state.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG
                                 | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;

    color_state.blendEnable = VK_TRUE;
    color_state.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha;
    color_state.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
    color_state.colorBlendOp = vk::BlendOp::eAdd;
    color_state.srcAlphaBlendFactor = vk::BlendFactor::eOne;
    color_state.dstAlphaBlendFactor = vk::BlendFactor::eZero;
    color_state.alphaBlendOp = vk::BlendOp::eAdd;

    vk::PipelineColorBlendStateCreateInfo color_blending_create_info;
    color_blending_create_info.logicOpEnable = VK_FALSE;
    color_blending_create_info.attachmentCount = 1;
    color_blending_create_info.pAttachments = &color_state;

    vk::PipelineLayoutCreateInfo pipeline_layout_create_info;
    pipeline_layout_create_info.setLayoutCount = static_cast<uint32_t>(descriptorSetLayouts.size());
    pipeline_layout_create_info.pSetLayouts = descriptorSetLayouts.data();
    pipeline_layout_create_info.pushConstantRangeCount = 1;
    pipeline_layout_create_info.pPushConstantRanges = &push_constant_range;

    auto layout_result = device->getLogicalDevice().createPipelineLayout(pipeline_layout_create_info);
    if (layout_result.result == vk::Result::eSuccess) {
        pipeline_layout = layout_result.value;
    } else {
        ASSERT_VULKAN(static_cast<VkResult>(layout_result.result), "Failed to create pipeline layout!")
    }

    vk::PipelineDepthStencilStateCreateInfo depth_stencil_create_info;
    depth_stencil_create_info.depthTestEnable = VK_TRUE;
    depth_stencil_create_info.depthWriteEnable = VK_TRUE;
    depth_stencil_create_info.depthCompareOp = vk::CompareOp::eLess;
    depth_stencil_create_info.depthBoundsTestEnable = VK_FALSE;
    depth_stencil_create_info.stencilTestEnable = VK_FALSE;

    vk::GraphicsPipelineCreateInfo graphics_pipeline_create_info;
    graphics_pipeline_create_info.stageCount = static_cast<uint32_t>(shader_stages.size());
    graphics_pipeline_create_info.pStages = shader_stages.data();
    graphics_pipeline_create_info.pVertexInputState = &vertex_input_create_info;
    graphics_pipeline_create_info.pInputAssemblyState = &input_assembly;
    graphics_pipeline_create_info.pViewportState = &viewport_state_create_info;
    graphics_pipeline_create_info.pDynamicState = nullptr;
    graphics_pipeline_create_info.pRasterizationState = &rasterizer_create_info;
    graphics_pipeline_create_info.pMultisampleState = &multisample_create_info;
    graphics_pipeline_create_info.pColorBlendState = &color_blending_create_info;
    graphics_pipeline_create_info.pDepthStencilState = &depth_stencil_create_info;
    graphics_pipeline_create_info.layout = pipeline_layout;
    graphics_pipeline_create_info.renderPass = render_pass;
    graphics_pipeline_create_info.subpass = 0;
    graphics_pipeline_create_info.basePipelineHandle = nullptr;
    graphics_pipeline_create_info.basePipelineIndex = -1;

    auto pipeline_result = device->getLogicalDevice().createGraphicsPipelines(nullptr, graphics_pipeline_create_info);
    if (pipeline_result.result == vk::Result::eSuccess) {
        graphics_pipeline = pipeline_result.value.front();
    } else {
        ASSERT_VULKAN(static_cast<VkResult>(pipeline_result.result), "Failed to create a graphics pipeline!")
    }

    device->getLogicalDevice().destroyShaderModule(vertex_shader_module);
    device->getLogicalDevice().destroyShaderModule(fragment_shader_module);
}