Program Listing for File Rasterizer.cpp
↰ Return to documentation for file (Src/GraphicsEngineVulkan/renderer/Rasterizer.cpp
)
#include "Rasterizer.hpp"
#include <array>
#include <filesystem>
#include <vector>
#include "common/FormatHelper.hpp"
#include "scene/Vertex.hpp"
#include "util/File.hpp"
#include "vulkan_base/ShaderHelper.hpp"
#include "common/Utilities.hpp"
#include "renderer/VulkanRendererConfig.hpp"
Kataglyphis::VulkanRendererInternals::Rasterizer::Rasterizer() {}
void Kataglyphis::VulkanRendererInternals::Rasterizer::init(VulkanDevice *device,
VulkanSwapChain *vulkanSwapChain,
const std::vector<VkDescriptorSetLayout> &descriptorSetLayouts,
VkCommandPool &commandPool)
{
this->device = device;
this->vulkanSwapChain = vulkanSwapChain;
createTextures(commandPool);
createRenderPass();
createPushConstantRange();
createGraphicsPipeline(descriptorSetLayouts);
createFramebuffer();
}
void Kataglyphis::VulkanRendererInternals::Rasterizer::shaderHotReload(
const std::vector<VkDescriptorSetLayout> &descriptor_set_layouts)
{
vkDestroyPipeline(device->getLogicalDevice(), graphics_pipeline, nullptr);
createGraphicsPipeline(descriptor_set_layouts);
}
Kataglyphis::Texture &Kataglyphis::VulkanRendererInternals::Rasterizer::getOffscreenTexture(uint32_t index)
{
return offscreenTextures[index];
}
void Kataglyphis::VulkanRendererInternals::Rasterizer::setPushConstant(PushConstantRasterizer pushConstant)
{
this->pushConstant = pushConstant;
}
void Kataglyphis::VulkanRendererInternals::Rasterizer::recordCommands(VkCommandBuffer &commandBuffer,
uint32_t image_index,
Scene *scene,
const std::vector<VkDescriptorSet> &descriptorSets)
{
// information about how to begin a render pass (only needed for graphical
// applications)
VkRenderPassBeginInfo render_pass_begin_info{};
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_begin_info.renderPass = render_pass;
render_pass_begin_info.renderArea.offset = { 0, 0 };
const VkExtent2D &swap_chain_extent = vulkanSwapChain->getSwapChainExtent();
render_pass_begin_info.renderArea.extent = swap_chain_extent;
// make sure the order you put the values into the array matches with the
// attchment order you have defined previous
std::array<VkClearValue, 2> clear_values = {};
clear_values[0].color = { 0.2f, 0.65f, 0.4f, 1.0f };
clear_values[1].depthStencil = { 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];
// begin render pass
vkCmdBeginRenderPass(commandBuffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
// bind pipeline to be used in render pass
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
for (uint32_t m = 0; m < static_cast<uint32_t>(scene->getModelCount()); m++) {
// for GCC doen't allow references on rvalues go like that ...
pushConstant.model = scene->getModelMatrix(0);
// just "Push" constants to given shader stage directly (no buffer)
vkCmdPushConstants(commandBuffer,
pipeline_layout,
VK_SHADER_STAGE_VERTEX_BIT,// stage to push constants to
0,// offset to push constants to update
sizeof(PushConstantRasterizer),// size of data being pushed
&pushConstant);// using model of current mesh (can be array)
for (unsigned int k = 0; k < scene->getMeshCount(m); k++) {
// list of vertex buffers we want to draw
VkBuffer vertex_buffers[] = { scene->getVertexBuffer(m, k) };// buffers to bind
VkDeviceSize offsets[] = { 0 };
vkCmdBindVertexBuffers(commandBuffer,
0,
1,
vertex_buffers,
offsets);// command to bind vertex buffer before drawing with them
// bind mesh index buffer with 0 offset and using the uint32 type
vkCmdBindIndexBuffer(commandBuffer, scene->getIndexBuffer(m, k), 0, VK_INDEX_TYPE_UINT32);
// bind descriptor sets
vkCmdBindDescriptorSets(commandBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline_layout,
0,
static_cast<uint32_t>(descriptorSets.size()),
descriptorSets.data(),
0,
nullptr);
// execute pipeline
vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(scene->getIndexCount(m, k)), 1, 0, 0, 0);
}
}
// end render pass
vkCmdEndRenderPass(commandBuffer);
}
void Kataglyphis::VulkanRendererInternals::Rasterizer::cleanUp()
{
for (auto framebuffer : framebuffer) { vkDestroyFramebuffer(device->getLogicalDevice(), framebuffer, nullptr); }
for (Texture texture : offscreenTextures) { texture.cleanUp(); }
depthBufferImage.cleanUp();
vkDestroyPipeline(device->getLogicalDevice(), graphics_pipeline, nullptr);
vkDestroyPipelineLayout(device->getLogicalDevice(), pipeline_layout, nullptr);
vkDestroyRenderPass(device->getLogicalDevice(), render_pass, nullptr);
}
Kataglyphis::VulkanRendererInternals::Rasterizer::~Rasterizer() {}
void Kataglyphis::VulkanRendererInternals::Rasterizer::createRenderPass()
{
// Color attachment of render pass
VkAttachmentDescription color_attachment{};
const VkFormat &swap_chain_image_format = vulkanSwapChain->getSwapChainFormat();
color_attachment.format = swap_chain_image_format;// format to use for attachment
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;// number of samples to write for multisampling
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;// describes what to do with attachment
// before rendering
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;// describes what to do with attachment
// after rendering
color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;// describes what to do with stencil
// before rendering
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;// describes what to do with stencil
// after rendering
// framebuffer data will be stored as an image, but images can be given
// different layouts to give optimal use for certain operations
color_attachment.initialLayout = VK_IMAGE_LAYOUT_GENERAL;// image data layout before render pass starts
color_attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL;// image data layout after render pass (to
// change to)
// depth attachment of render pass
VkAttachmentDescription depth_attachment{};
depth_attachment.format = choose_supported_format(device->getPhysicalDevice(),
{ VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_D24_UNORM_S8_UINT },
VK_IMAGE_TILING_OPTIMAL,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
depth_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depth_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
depth_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depth_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
depth_attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
// attachment reference uses an attachment index that refers to index in the
// attachment list passed to renderPassCreateInfo
VkAttachmentReference color_attachment_reference{};
color_attachment_reference.attachment = 0;
color_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
// attachment reference
VkAttachmentReference depth_attachment_reference{};
depth_attachment_reference.attachment = 1;
depth_attachment_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
// information about a particular subpass the render pass is using
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;// pipeline type subpass is to be bound
// to
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachment_reference;
subpass.pDepthStencilAttachment = &depth_attachment_reference;
// need to determine when layout transitions occur using subpass dependencies
std::array<VkSubpassDependency, 1> subpass_dependencies;
// conversion from VK_IMAGE_LAYOUT_UNDEFINED to
// VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL transition must happen after ....
subpass_dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;// subpass index (VK_SUBPASS_EXTERNAL = Special
// value meaning outside of renderpass)
subpass_dependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;// pipeline stage
subpass_dependencies[0].srcAccessMask = 0;// stage access mask (memory access)
// but must happen before ...
subpass_dependencies[0].dstSubpass = 0;
subpass_dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
subpass_dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
subpass_dependencies[0].dependencyFlags = 0;// VK_DEPENDENCY_BY_REGION_BIT;
std::array<VkAttachmentDescription, 2> render_pass_attachments = { color_attachment, depth_attachment };
// create info for render pass
VkRenderPassCreateInfo render_pass_create_info{};
render_pass_create_info.sType = VK_STRUCTURE_TYPE_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();
VkResult result = vkCreateRenderPass(device->getLogicalDevice(), &render_pass_create_info, nullptr, &render_pass);
ASSERT_VULKAN(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<VkImageView, 2> attachments = { offscreenTextures[i].getImageView(),
depthBufferImage.getImageView() };
VkFramebufferCreateInfo frame_buffer_create_info{};
frame_buffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_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 VkExtent2D &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;
VkResult result =
vkCreateFramebuffer(device->getLogicalDevice(), &frame_buffer_create_info, nullptr, &framebuffer[i]);
ASSERT_VULKAN(result, "Failed to create framebuffer!");
}
}
void Kataglyphis::VulkanRendererInternals::Rasterizer::createPushConstantRange()
{
// define push constant values (no 'create' needed)
push_constant_range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
push_constant_range.offset = 0;
push_constant_range.size = sizeof(PushConstantRasterizer);
}
void Kataglyphis::VulkanRendererInternals::Rasterizer::createTextures(VkCommandPool &commandPool)
{
offscreenTextures.resize(vulkanSwapChain->getNumberSwapChainImages());
VkCommandBuffer cmdBuffer = commandBufferManager.beginCommandBuffer(device->getLogicalDevice(), commandPool);
for (uint32_t index = 0; index < static_cast<uint32_t>(vulkanSwapChain->getNumberSwapChainImages()); index++) {
Texture texture{};
const VkExtent2D &swap_chain_extent = vulkanSwapChain->getSwapChainExtent();
const VkFormat &swap_chain_image_format = vulkanSwapChain->getSwapChainFormat();
texture.createImage(device,
swap_chain_extent.width,
swap_chain_extent.height,
1,
swap_chain_image_format,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT
| VK_IMAGE_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
texture.createImageView(device, swap_chain_image_format, VK_IMAGE_ASPECT_COLOR_BIT, 1);
// --- WE NEED A DIFFERENT LAYOUT FOR USAGE
VulkanImage &image = texture.getVulkanImage();
image.transitionImageLayout(
cmdBuffer, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, 1, VK_IMAGE_ASPECT_COLOR_BIT);
offscreenTextures[index] = texture;
}
VkFormat depth_format = choose_supported_format(device->getPhysicalDevice(),
{ VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_D24_UNORM_S8_UINT },
VK_IMAGE_TILING_OPTIMAL,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
// create depth buffer image
// MIP LEVELS: for depth texture we only want 1 level :)
const VkExtent2D &swap_chain_extent = vulkanSwapChain->getSwapChainExtent();
depthBufferImage.createImage(device,
swap_chain_extent.width,
swap_chain_extent.height,
1,
depth_format,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
// depth buffer image view
// MIP LEVELS: for depth texture we only want 1 level :)
depthBufferImage.createImageView(device, depth_format, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, 1);
// --- WE NEED A DIFFERENT LAYOUT FOR USAGE
VulkanImage &vulkanImage = depthBufferImage.getVulkanImage();
vulkanImage.transitionImageLayout(device->getLogicalDevice(),
device->getGraphicsQueue(),
commandPool,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
1);
commandBufferManager.endAndSubmitCommandBuffer(
device->getLogicalDevice(), commandPool, device->getGraphicsQueue(), cmdBuffer);
}
void Kataglyphis::VulkanRendererInternals::Rasterizer::createGraphicsPipeline(
const std::vector<VkDescriptorSetLayout> &descriptorSetLayouts)
{
std::stringstream rasterizer_shader_dir;
std::filesystem::path 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> vertex_shader_code = vertexFile.readCharSequence();
std::vector<char> fragment_shader_code = fragmentFile.readCharSequence();
// build shader modules to link to graphics pipeline
VkShaderModule vertex_shader_module = shaderHelper.createShaderModule(device, vertex_shader_code);
VkShaderModule fragment_shader_module = shaderHelper.createShaderModule(device, fragment_shader_code);
// shader stage creation information
// vertex stage creation information
VkPipelineShaderStageCreateInfo vertex_shader_create_info{};
vertex_shader_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertex_shader_create_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertex_shader_create_info.module = vertex_shader_module;
vertex_shader_create_info.pName = "main";
// fragment stage creation information
VkPipelineShaderStageCreateInfo fragment_shader_create_info{};
fragment_shader_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragment_shader_create_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragment_shader_create_info.module = fragment_shader_module;
fragment_shader_create_info.pName = "main";
std::vector<VkPipelineShaderStageCreateInfo> shader_stages = { vertex_shader_create_info,
fragment_shader_create_info };
// how the data for a single vertex (including info such as position, color,
// texture coords, normals, etc) is as a whole
VkVertexInputBindingDescription binding_description{};
binding_description.binding = 0;
binding_description.stride = sizeof(Vertex);
binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;// how to move between data after each
// vertex.
// how the data for an attribute is defined within a vertex
std::array<VkVertexInputAttributeDescription, 4> attribute_describtions = vertex::getVertexInputAttributeDesc();
// CREATE PIPELINE
// 1.) Vertex input
VkPipelineVertexInputStateCreateInfo vertex_input_create_info{};
vertex_input_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_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();
// input assembly
VkPipelineInputAssemblyStateCreateInfo input_assembly{};
input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
input_assembly.primitiveRestartEnable = VK_FALSE;
// viewport & scissor
// create a viewport info struct
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
const VkExtent2D &swap_chain_extent = vulkanSwapChain->getSwapChainExtent();
viewport.width = (float)swap_chain_extent.width;
viewport.height = (float)swap_chain_extent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
// create a scissor info struct
VkRect2D scissor{};
scissor.offset = { 0, 0 };
scissor.extent = swap_chain_extent;
VkPipelineViewportStateCreateInfo viewport_state_create_info{};
viewport_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_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;
// RASTERIZER
VkPipelineRasterizationStateCreateInfo rasterizer_create_info{};
rasterizer_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer_create_info.depthClampEnable = VK_FALSE;
rasterizer_create_info.rasterizerDiscardEnable = VK_FALSE;
rasterizer_create_info.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer_create_info.lineWidth = 1.0f;
rasterizer_create_info.cullMode = VK_CULL_MODE_BACK_BIT;//
// winding to determine which side is front; y-coordinate is inverted in
// comparison to OpenGL
rasterizer_create_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizer_create_info.depthBiasClamp = VK_FALSE;
// -- MULTISAMPLING --
VkPipelineMultisampleStateCreateInfo multisample_create_info{};
multisample_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisample_create_info.sampleShadingEnable = VK_FALSE;
multisample_create_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
// -- BLENDING --
// blend attachment state
VkPipelineColorBlendAttachmentState color_state{};
color_state.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
color_state.blendEnable = VK_TRUE;
// blending uses equation: (srcColorBlendFactor * new_color) color_blend_op
// (dstColorBlendFactor * old_color)
color_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
color_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
color_state.colorBlendOp = VK_BLEND_OP_ADD;
color_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
color_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
color_state.alphaBlendOp = VK_BLEND_OP_ADD;
VkPipelineColorBlendStateCreateInfo color_blending_create_info{};
color_blending_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
color_blending_create_info.logicOpEnable = VK_FALSE;
color_blending_create_info.attachmentCount = 1;
color_blending_create_info.pAttachments = &color_state;
// -- PIPELINE LAYOUT --
VkPipelineLayoutCreateInfo pipeline_layout_create_info{};
pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_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;
// create pipeline layout
VkResult result =
vkCreatePipelineLayout(device->getLogicalDevice(), &pipeline_layout_create_info, nullptr, &pipeline_layout);
ASSERT_VULKAN(result, "Failed to create pipeline layout!")
// -- DEPTH STENCIL TESTING --
VkPipelineDepthStencilStateCreateInfo depth_stencil_create_info{};
depth_stencil_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depth_stencil_create_info.depthTestEnable = VK_TRUE;
depth_stencil_create_info.depthWriteEnable = VK_TRUE;
depth_stencil_create_info.depthCompareOp = VK_COMPARE_OP_LESS;
depth_stencil_create_info.depthBoundsTestEnable = VK_FALSE;
depth_stencil_create_info.stencilTestEnable = VK_FALSE;
// -- GRAPHICS PIPELINE CREATION --
VkGraphicsPipelineCreateInfo graphics_pipeline_create_info{};
graphics_pipeline_create_info.sType = VK_STRUCTURE_TYPE_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;
// pipeline derivatives : can create multiple pipelines that derive from one
// another for optimization
graphics_pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
graphics_pipeline_create_info.basePipelineIndex = -1;
// create graphics pipeline
result = vkCreateGraphicsPipelines(
device->getLogicalDevice(), VK_NULL_HANDLE, 1, &graphics_pipeline_create_info, nullptr, &graphics_pipeline);
ASSERT_VULKAN(result, "Failed to create a graphics pipeline!")
// Destroy shader modules, no longer needed after pipeline created
vkDestroyShaderModule(device->getLogicalDevice(), vertex_shader_module, nullptr);
vkDestroyShaderModule(device->getLogicalDevice(), fragment_shader_module, nullptr);
}