text: #include "pch.h"
  
  #include "phm_pipeline.h"
  #include "phm_model.h"
  
  #include <fstream>
  #include <stdexcept>
  #include <cassert>
  
  
  namespace phm
  {
  	PhmPipeline::PhmPipeline(
  		PhmDevice& device,
  		const std::string& vertFilePath,
  		const std::string& fragFilePath,
  		const PipelineConfigInfo& configInfo
  	)
  		: device_(device)
  	{
  		createGraphicsPipeline(vertFilePath, fragFilePath, configInfo);
  	}
  
  	PhmPipeline::~PhmPipeline()
  	{
  		vkDestroyShaderModule(device_.device(), vertexShaderModule_, nullptr);
  		vkDestroyShaderModule(device_.device(), fragmentShaderModule_, nullptr);
  		vkDestroyPipeline(device_.device(), graphicsPipeline_, nullptr);
  	}
  
  	void PhmPipeline::createGraphicsPipeline(const std::string& vertFilePath, const std::string& fragFilePath, const PipelineConfigInfo& configInfo)
  	{
  		assert(
  			configInfo.pipelineLayout != VK_NULL_HANDLE &&
  			"Unable to create graphics pipeline: No pipelineLayout provided in configInfo"
  		);
  		assert(
  			configInfo.renderPass != VK_NULL_HANDLE &&
  			"Unable to create graphics pipeline: No renderpass provided in configInfo"
  		);
  
  		// Read in the vertex and fragment shader code.
  		std::vector<char> vertCode = readFile(vertFilePath);
  		std::vector<char> fragCode = readFile(fragFilePath);
  
  		createShaderModule(vertCode, &vertexShaderModule_);
  		createShaderModule(fragCode, &fragmentShaderModule_);
  
  		VkPipelineShaderStageCreateInfo shaderStages[2];
  		// Specify the vertex shader
  		shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
  		shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
  		shaderStages[0].module = vertexShaderModule_;
  		shaderStages[0].pName = "main";
  		shaderStages[0].flags = 0;
  		shaderStages[0].pNext = nullptr;
  		shaderStages[0].pSpecializationInfo = nullptr;
  
  		// Specify the fragment shader
  		shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
  		shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
  		shaderStages[1].module = fragmentShaderModule_;
  		shaderStages[1].pName = "main";
  		shaderStages[1].flags = 0;
  		shaderStages[1].pNext = nullptr;
  		shaderStages[1].pSpecializationInfo = nullptr;
  
  		// Initiate the vertex input info
  		auto& bindingDescriptions = configInfo.bindingDescriptions;
  		auto& attributeDescriptions = configInfo.attributeDescriptions;
  
  		VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
  		vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
  		vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
  		vertexInputInfo.vertexBindingDescriptionCount = static_cast<uint32_t>(bindingDescriptions.size());
  		vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
  		vertexInputInfo.pVertexBindingDescriptions = bindingDescriptions.data();
  
  		// Create the actual pipeline creation info.
  		VkGraphicsPipelineCreateInfo pipelineInfo{};
  		pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
  		pipelineInfo.stageCount = 2;
  		pipelineInfo.pStages = shaderStages;
  		pipelineInfo.pVertexInputState = &vertexInputInfo;
  		pipelineInfo.pInputAssemblyState = &configInfo.inputAssemblyInfo;
  		pipelineInfo.pViewportState = &configInfo.viewportInfo;
  		pipelineInfo.pDepthStencilState = &configInfo.depthStencilInfo;
  		pipelineInfo.pColorBlendState = &configInfo.colorBlendInfo;
  		pipelineInfo.pRasterizationState = &configInfo.rasterizationInfo;
  		pipelineInfo.pMultisampleState = &configInfo.multisampleInfo;
  		pipelineInfo.pDynamicState = &configInfo.dynamicStateInfo;
  
  		pipelineInfo.layout = configInfo.pipelineLayout;
  		pipelineInfo.renderPass = configInfo.renderPass;
  		pipelineInfo.subpass = configInfo.subpass;
  
  		pipelineInfo.basePipelineIndex = -1;
  		pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
  
  		if (vkCreateGraphicsPipelines(device_.device(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline_) != VK_SUCCESS)
  		{
  			throw std::runtime_error("Failed to create graphics pipeline");
  		}
  
  	}
  
  	std::vector<char> PhmPipeline::readFile(const std::string& filename)
  	{
  		// Read in binary and start at the end of the file
  		std::ifstream file(filename, std::ios::ate | std::ios::binary);
  
  		// Try to open the file
  		if (!file.is_open())
  		{
  			throw std::runtime_error("failed to open file!");
  		}
  
  		// Alocate a buffer of the right size
  		size_t fileSize = (size_t)file.tellg();
  		std::vector<char> buffer(fileSize);
  
  		// Seek back to the beginning and read all of the bytes at once
  		file.seekg(0);
  		file.read(buffer.data(), fileSize);
  
  		// Close the file
  		file.close();
  
  		// Return the buffer.
  		return buffer;
  	}
  
  	void PhmPipeline::createShaderModule(const std::vector<char>& code, VkShaderModule* shaderModule)
  	{
  		VkShaderModuleCreateInfo createInfo{};
  		createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
  		createInfo.codeSize = code.size();
  		createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
  
  		if (vkCreateShaderModule(device_.device(), &createInfo, nullptr, shaderModule) != VK_SUCCESS)
  		{
  			throw std::runtime_error("Failed to create shader module");
  		}
  	}
  
  	void PhmPipeline::bind(VkCommandBuffer commandBuffer)
  	{
  		vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline_);
  	}
  
  	/// <summary>
  	/// Takes in a reference to a configInfo object and writes default values to it.
  	/// </summary>
  	/// <param name="configInfo">The reference to be written to. </param>
  	void phm::PhmPipeline::defaultPipelineConfigInfo(PipelineConfigInfo& configInfo)
  	{
  		// Initiate the input assembly info.
  		configInfo.inputAssemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
  		// We use triangles as primitive.
  		configInfo.inputAssemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
  		// Must be false for the topology.
  		configInfo.inputAssemblyInfo.primitiveRestartEnable = VK_FALSE;
  
  		// Initiate the viewport info
  		configInfo.viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
  		configInfo.viewportInfo.viewportCount = 1;
  		configInfo.viewportInfo.pViewports = nullptr;
  		configInfo.viewportInfo.scissorCount = 1;
  		configInfo.viewportInfo.pScissors = nullptr;
  
  		// Initiate the rasterization info
  		configInfo.rasterizationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
  		configInfo.rasterizationInfo.depthClampEnable = VK_FALSE;			// Option to clamp the z component to be between 0 and 1.
  		configInfo.rasterizationInfo.rasterizerDiscardEnable = VK_FALSE;
  		configInfo.rasterizationInfo.polygonMode = VK_POLYGON_MODE_FILL;
  		configInfo.rasterizationInfo.lineWidth = 1.0f;
  		configInfo.rasterizationInfo.cullMode = VK_CULL_MODE_NONE;			// Option to cull triangles that aren't frontfacing.
  		configInfo.rasterizationInfo.frontFace = VK_FRONT_FACE_CLOCKWISE;	// specify what frontfacing means.
  		configInfo.rasterizationInfo.depthBiasEnable = VK_FALSE;
  		configInfo.rasterizationInfo.depthBiasConstantFactor = 0.0f;
  		configInfo.rasterizationInfo.depthBiasClamp = 0.0f;
  		configInfo.rasterizationInfo.depthBiasSlopeFactor = 0.0f;
  
  		// Initiate the multisample info
  		configInfo.multisampleInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
  		configInfo.multisampleInfo.sampleShadingEnable = VK_FALSE;			// Multisampling is turned off for now.
  		configInfo.multisampleInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
  		configInfo.multisampleInfo.minSampleShading = 1.0f;
  		configInfo.multisampleInfo.pSampleMask = nullptr;
  		configInfo.multisampleInfo.alphaToCoverageEnable = VK_FALSE;
  		configInfo.multisampleInfo.alphaToOneEnable = VK_FALSE;
  
  		// Initiate the color blend attachment.
  		configInfo.colorBlendAttachment.colorWriteMask =
  			VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
  			VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
  		configInfo.colorBlendAttachment.blendEnable = VK_TRUE;
  		configInfo.colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
  		configInfo.colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
  		configInfo.colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
  		configInfo.colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
  		configInfo.colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
  		configInfo.colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
  
  		// Initiate the color blend info
  		configInfo.colorBlendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
  		configInfo.colorBlendInfo.logicOpEnable = VK_FALSE;
  		configInfo.colorBlendInfo.logicOp = VK_LOGIC_OP_COPY;
  		configInfo.colorBlendInfo.attachmentCount = 1;
  		configInfo.colorBlendInfo.pAttachments = &configInfo.colorBlendAttachment;
  		configInfo.colorBlendInfo.blendConstants[0] = 0.0f;
  		configInfo.colorBlendInfo.blendConstants[1] = 0.0f;
  		configInfo.colorBlendInfo.blendConstants[2] = 0.0f;
  		configInfo.colorBlendInfo.blendConstants[3] = 0.0f;
  
  		// Initiate the depth stencil info
  		configInfo.depthStencilInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
  		configInfo.depthStencilInfo.depthTestEnable = VK_TRUE;
  		configInfo.depthStencilInfo.depthWriteEnable = VK_TRUE;
  		configInfo.depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS;
  		configInfo.depthStencilInfo.depthBoundsTestEnable = VK_FALSE;
  		configInfo.depthStencilInfo.minDepthBounds = 0.0f;
  		configInfo.depthStencilInfo.maxDepthBounds = 1.0f;
  		configInfo.depthStencilInfo.stencilTestEnable = VK_FALSE;
  		configInfo.depthStencilInfo.front = {};
  		configInfo.depthStencilInfo.back = {};
  
  		// Initiate the dynamic state info
  		configInfo.dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
  		configInfo.dynamicStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
  		configInfo.dynamicStateInfo.pDynamicStates = configInfo.dynamicStateEnables.data();
  		configInfo.dynamicStateInfo.dynamicStateCount = static_cast<uint32_t>(configInfo.dynamicStateEnables.size());
  		configInfo.dynamicStateInfo.flags = 0;
  
  		configInfo.bindingDescriptions = PhmModel::Vertex::getBindingDescriptions();
  		configInfo.attributeDescriptions = PhmModel::Vertex::getAttributeDescriptions();
  	}
  } // namespace phm