Newer
Older
ForwardPlusRenderer / src / Pipeline.cpp
#include <stdafx.h>
#include "Pipeline.h"
#include "Context.h"
#include "Device.h"
#include "RenderGraph.h"
#include "Vertex.h"
#include "IO.h"
#include "SwapChain.h"


#define FRAGMENT_SHADER_PATH "Shaders/FragmentShader.spv"
#define VERTEX_SHADER_PATH "Shaders/VertexShader.spv"
#define DEPTH_SHADER_PATH "Shaders/EarlyZ.spv"
#define COMPUTE_SHADER_PATH "Shaders/LightComputeShader.spv"

namespace fpr
{
PipelineOptions Pipeline::GetDefaultGraphicsPipeline()
{
  //The default graphics pipeline for the renderer. Has no significant options eenabled, most are default.
  PipelineOptions options;
  auto&           context         = fpr::Context::Get();
  auto            vertex_shader   = fpr::ReadFile(VERTEX_SHADER_PATH);
  auto            fragment_shader = fpr::ReadFile(FRAGMENT_SHADER_PATH);

  //This pipeline handles simple vertex transformation and fragment shading.
  vk::ShaderModule vertex_shader_module   = CreateShaderModule(vertex_shader);
  vk::ShaderModule fragment_shader_module = CreateShaderModule(fragment_shader);

  options.modules = { { vertex_shader_module, vk::ShaderStageFlagBits::eVertex },
                      { fragment_shader_module, vk::ShaderStageFlagBits::eFragment } };

  vk::PipelineInputAssemblyStateCreateInfo input_assembly_create_info;
  input_assembly_create_info.topology               = vk::PrimitiveTopology::eTriangleList;
  input_assembly_create_info.primitiveRestartEnable = false;

  vk::PushConstantRange push_constant_range;
  push_constant_range.stageFlags = vk::ShaderStageFlagBits::eFragment;
  push_constant_range.offset     = 0;
  push_constant_range.size       = sizeof(ViewportConstants);

  options.push_constant_ranges.push_back(push_constant_range);

  vk::PipelineRasterizationStateCreateInfo rasterizer_create_info{};
  rasterizer_create_info.depthClampEnable        = false;
  rasterizer_create_info.rasterizerDiscardEnable = 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.depthBiasEnable         = false;

  vk::PipelineMultisampleStateCreateInfo multisample_create_info{};
  multisample_create_info.sampleShadingEnable  = false;
  multisample_create_info.rasterizationSamples = vk::SampleCountFlagBits::e1;

  vk::PipelineColorBlendAttachmentState blend_attach_state{};
  blend_attach_state.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
      vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
  blend_attach_state.alphaBlendOp = vk::BlendOp::eAdd;

  //Buffers to use in the shaders specified in the shader modules above.
  std::vector<vk::DescriptorSetLayout> layouts = { context.m_render_graph->DescriptorSetLayouts.Get("Camera"),
                                                   context.m_render_graph->DescriptorSetLayouts.Get("Model"),
                                                   context.m_render_graph->DescriptorSetLayouts.Get("Sampler"),
                                                   context.m_render_graph->DescriptorSetLayouts.Get("PointLight") };

  std::vector<vk::DynamicState> dynamic_states = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };

  vk::PipelineDepthStencilStateCreateInfo depth_stencil_ceate_info{};

  depth_stencil_ceate_info.depthTestEnable       = true;
  depth_stencil_ceate_info.depthWriteEnable      = false;
  depth_stencil_ceate_info.depthCompareOp        = vk::CompareOp::eLessOrEqual;
  depth_stencil_ceate_info.depthBoundsTestEnable = false;
  depth_stencil_ceate_info.stencilTestEnable     = false;

  options.input_assembly      = input_assembly_create_info;
  options.rasterizer          = rasterizer_create_info;
  options.multisample         = multisample_create_info;
  options.blend_attach_states = { blend_attach_state };
  options.depth_stencil       = depth_stencil_ceate_info;
  options.layouts             = layouts;
  options.render_pass         = context.m_render_graph->RenderPasses.Get("Default")->GetvkRenderPass();
  options.dynamic_states      = dynamic_states;
  options.flags               = vk::PipelineCreateFlagBits::eAllowDerivatives;

  return options;
}

PipelineOptions Pipeline::GetDepthPassPipeline()
{
  PipelineOptions options;
  auto&           context        = fpr::Context::Get();
  auto            early_z_shader = fpr::ReadFile(DEPTH_SHADER_PATH);

  //This pipeline is a depth-prepass, only vertex shader required,
  vk::ShaderModule early_z_module = CreateShaderModule(early_z_shader);

  options.modules = { { early_z_module, vk::ShaderStageFlagBits::eVertex } };

  vk::PipelineInputAssemblyStateCreateInfo input_assembly_create_info;
  input_assembly_create_info.topology               = vk::PrimitiveTopology::eTriangleList;
  input_assembly_create_info.primitiveRestartEnable = false;


  vk::PipelineRasterizationStateCreateInfo rasterizer_create_info{};
  rasterizer_create_info.depthClampEnable        = false;
  rasterizer_create_info.rasterizerDiscardEnable = 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.depthBiasEnable         = false;

  vk::PipelineMultisampleStateCreateInfo multisample_create_info{};
  multisample_create_info.sampleShadingEnable  = false;
  multisample_create_info.rasterizationSamples = vk::SampleCountFlagBits::e1;

  vk::PipelineColorBlendAttachmentState blend_attach_state{};
  blend_attach_state.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
      vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;

  //No texture data needed for the depth prepass.
  std::vector<vk::DescriptorSetLayout> layouts = { context.m_render_graph->DescriptorSetLayouts.Get("Camera"),
                                                   context.m_render_graph->DescriptorSetLayouts.Get("Model")
                                                   };

  vk::PipelineDepthStencilStateCreateInfo depth_stencil_ceate_info{};

  depth_stencil_ceate_info.depthTestEnable       = true;
  depth_stencil_ceate_info.depthWriteEnable      = true;
  depth_stencil_ceate_info.depthCompareOp        = vk::CompareOp::eLess;
  depth_stencil_ceate_info.depthBoundsTestEnable = false;
  depth_stencil_ceate_info.stencilTestEnable     = false;

  std::vector<vk::DynamicState> dynamic_states = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };

  options.input_assembly      = input_assembly_create_info;
  options.rasterizer          = rasterizer_create_info;
  options.multisample         = multisample_create_info;
  options.blend_attach_states = { blend_attach_state };
  options.depth_stencil       = depth_stencil_ceate_info;
  options.layouts             = layouts;
  options.render_pass         = context.m_render_graph->RenderPasses.Get("EarlyZ")->GetvkRenderPass();
  options.flags               = vk::PipelineCreateFlagBits::eDerivative;
  options.dynamic_states      = dynamic_states;

  return options;
}

PipelineOptions Pipeline::GetLightCullingPipeline()
{
  auto&           context = fpr::Context::Get();
  PipelineOptions options;

  auto compute_shader = fpr::ReadFile(COMPUTE_SHADER_PATH);
  vk::ShaderModule compute_shader_module = CreateShaderModule(compute_shader);

  vk::PushConstantRange push_constant_range;
  push_constant_range.stageFlags = vk::ShaderStageFlagBits::eCompute;
  push_constant_range.offset     = 0;
  push_constant_range.size       = sizeof(ViewportConstants);

  options.push_constant_ranges.push_back(push_constant_range);
  options.modules = { { compute_shader_module, vk::ShaderStageFlagBits::eCompute } };


  std::vector<vk::DescriptorSetLayout> layouts = { context.m_render_graph->DescriptorSetLayouts.Get("Camera"),
                                                   context.m_render_graph->DescriptorSetLayouts.Get("PointLight"),
                                                   context.m_render_graph->DescriptorSetLayouts.Get("DepthSampler") };

  options.layouts = layouts;

  return options;
}

Pipeline::Pipeline(const PipelineOptions& options, Pipeline* parent, EPipelineType type)
{
  auto& context = fpr::Context::Get();

  vk::PipelineLayoutCreateInfo layout_info;
  layout_info.setSetLayouts(options.layouts);
  layout_info.setPushConstantRanges(options.push_constant_ranges);

  auto [layout_result, layout] = context.GetDevice()->GetLogicalDeviceHandle().createPipelineLayoutUnique(layout_info);

  assert(layout_result == vk::Result::eSuccess);
  m_layout = std::move(layout);


  std::vector<vk::PipelineShaderStageCreateInfo> shader_stages;
  for(const auto& module : options.modules)
  {
    vk::PipelineShaderStageCreateInfo shader_stage;
    const auto& [shader_module, stage] = module;
    shader_stage.stage                 = stage;
    shader_stage.module                = shader_module;
    shader_stage.pName                 = "main";
    shader_stages.push_back(shader_stage);
  }


  m_options = options;


  vk::PipelineCacheCreateInfo cache_info;
  auto [cache_result, cache] = context.GetDevice()->GetLogicalDeviceHandle().createPipelineCacheUnique(cache_info);
  assert(cache_result == vk::Result::eSuccess);
  m_cache = std::move(cache);


  //Based on the given pipeline, construct it based on its options.
  switch(type)
  {
    case EPipelineType::EPT_Graphics:
    {
      vk::GraphicsPipelineCreateInfo create_info;
      if(parent != nullptr)
      {
        m_parent = parent;
        create_info.setBasePipelineHandle(*m_parent->m_pipeline);
      }
      create_info.setFlags(options.flags);
      auto [vertex_binding_desc, vertex_attr_desc] = Vertex::MakeVertexInputStateData();
      vk::PipelineVertexInputStateCreateInfo vertex_input_create_info{};

      vertex_input_create_info.vertexBindingDescriptionCount   = (uint32_t)vertex_binding_desc.size();
      vertex_input_create_info.pVertexBindingDescriptions      = vertex_binding_desc.data();
      vertex_input_create_info.vertexAttributeDescriptionCount = (uint32_t)vertex_attr_desc.size();
      vertex_input_create_info.pVertexAttributeDescriptions    = vertex_attr_desc.data();

      vk::PipelineColorBlendStateCreateInfo blend_state_create_info{};

      blend_state_create_info.setAttachments(options.blend_attach_states);

      vk::PipelineViewportStateCreateInfo viewport_state_create_info{};
      viewport_state_create_info.scissorCount  = 1;
      viewport_state_create_info.viewportCount = 1;

      vk::PipelineDynamicStateCreateInfo dynamic_state_info;
      dynamic_state_info.setDynamicStates(options.dynamic_states);


      create_info.setLayout(*m_layout);
      create_info.setPColorBlendState(&blend_state_create_info);
      create_info.setPDepthStencilState(&options.depth_stencil);
      create_info.setPDynamicState(&dynamic_state_info);
      create_info.setPInputAssemblyState(&options.input_assembly);
      create_info.setPMultisampleState(&options.multisample);
      create_info.setPRasterizationState(&options.rasterizer);
      create_info.setPVertexInputState(&vertex_input_create_info);
      create_info.setPViewportState(&viewport_state_create_info);
      create_info.setRenderPass(options.render_pass);
      create_info.setStages(shader_stages);
      create_info.setSubpass(options.subpass);
      create_info.basePipelineIndex = -1;


      auto [pipeline_result, pipeline] =
          context.GetDevice()->GetLogicalDeviceHandle().createGraphicsPipelineUnique(*m_cache, create_info);
      m_pipeline = std::move(pipeline);
      break;
    }

    case EPipelineType::EPT_Compute:
    {
      //Compute pipelines don't concern themselves with a viewport or rasterisation and so on.
      vk::ComputePipelineCreateInfo create_info{};
      create_info.setLayout(*m_layout).setStage(shader_stages[0]);
      auto [pipeline_result, pipeline] =
          context.GetDevice()->GetLogicalDeviceHandle().createComputePipelineUnique(*m_cache, create_info);
      m_pipeline = std::move(pipeline);
      break;
    }
  }

  //Shader modules are no longer needed, they have been loaded in already.
  for(auto& shader : shader_stages) context.GetDevice()->GetLogicalDeviceHandle().destroyShaderModule(shader.module);
}

vk::Pipeline& Pipeline::GetPipeline()
{
  return m_pipeline.get();
}

vk::PipelineLayout& Pipeline::GetLayout()
{
  return m_layout.get();
}

vk::ShaderModule CreateShaderModule(const std::vector<char>& shader)
{
  assert(("Shader malformed!", shader.size() % sizeof(uint32_t) == 0));

  vk::ShaderModuleCreateInfo shader_create_info{};
  shader_create_info.codeSize = shader.size();
  shader_create_info.pCode    = reinterpret_cast<const uint32_t*>(shader.data());

  auto [result_signal, shader_module] =
      fpr::Context::Get().GetDevice()->GetLogicalDeviceHandle().createShaderModule(shader_create_info);

  assert(("Shader module creation failed", result_signal == vk::Result::eSuccess));
  return shader_module;
}
} // namespace fpr