#ifndef PIPELINES_H #define PIPELINES_H // Normal header #include "Common/Types.h" #include #include #include #include #include namespace vkutil { bool loadShaderModule( const char* filePath, VkDevice device, VkShaderModule* outShaderModule ); }; class PipelineBuilder { public: std::vector _shaderStages; VkPipelineInputAssemblyStateCreateInfo _inputAssembly; VkPipelineRasterizationStateCreateInfo _rasterizer; VkPipelineColorBlendAttachmentState _colorBlendAttachment; VkPipelineMultisampleStateCreateInfo _multisampling; VkPipelineLayout _pipelineLayout; VkPipelineDepthStencilStateCreateInfo _depthStencil; VkPipelineRenderingCreateInfo _renderInfo; PipelineBuilder() { clear(); } void clear(); VkPipeline buildPipeline( VkDevice device ); void setShaders( VkShaderModule vertexShader, VkShaderModule fragmentShader ); void setInputTopology( VkPrimitiveTopology topology ); }; #endif #ifdef PIPELINES_IMPL #ifndef PIPELINES_IMPL_H #define PIPELINES_IMPL_H // Implementation // Imports #include // Custom imports #include "initializers.h" bool vkutil::loadShaderModule( const char* filePath, VkDevice device, VkShaderModule* outShaderModule ) { #ifdef DEBUG fmt::println("[Engine:Utils] Loading shader located at {}...", filePath); #endif // Open the file, with the cursor at the end std::ifstream file( filePath, std::ios::ate | std::ios::binary ); if (!file.is_open()) { return false; } // Find the size of the file by looking // at the cursor size_t fileSize = (size_t)file.tellg(); // Allocate a big enough vector to suit SPIR-V std::vector buffer(fileSize/ sizeof(uint32_t)); file.seekg(0); // Put the cursor at the begining // Load the file in the buffer file.read((char*)buffer.data(), fileSize); file.close(); // Create the shader module VkShaderModuleCreateInfo createInfo { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .pNext = nullptr, .codeSize = buffer.size() * sizeof(uint32_t), // Convert to bytes .pCode = buffer.data() }; // Check that everything went well VkShaderModule shaderModule; if (vkCreateShaderModule( device, &createInfo, nullptr, &shaderModule ) != VK_SUCCESS){ return false; } *outShaderModule = shaderModule; #ifdef DEBUG fmt::println("[Engine:Utils] Shader loaded from {} and size {}", filePath, fileSize); #endif return true; } void PipelineBuilder::clear() { #ifdef DEBUG fmt::println("[PipelineBuilder] Clearing pipeline..."); #endif // Clear structs _inputAssembly = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; _rasterizer = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; _colorBlendAttachment = {}; _multisampling = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; _pipelineLayout = {}; _depthStencil = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; _renderInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO }; _shaderStages.clear(); #ifdef DEBUG fmt::println("[PipelineBuilder] Pipeline cleared !"); #endif } VkPipeline PipelineBuilder::buildPipeline ( VkDevice device ) { #ifdef DEBUG fmt::println("[PipelineBuilder] Building rasterisation pipeline..."); #endif // Make the viewport state from our stored viewport and scissor // This makes it that it won't support multiple viewports or scissors VkPipelineViewportStateCreateInfo viewportState { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pNext = nullptr, .viewportCount = 1, .scissorCount = 1 }; // Setup placeholder color blending. // Need to be changed when using transparent objects, which we aren't rn // The blending is "no blend", but we write it to the color attachment VkPipelineColorBlendStateCreateInfo colorBlending { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .pNext = nullptr, .logicOpEnable = VK_FALSE, .logicOp = VK_LOGIC_OP_COPY, .attachmentCount = 1, .pAttachments = &_colorBlendAttachment }; // Completely clear VertexInputStateCreateInfo, as we have no need for it VkPipelineVertexInputStateCreateInfo _vertexInputInfo { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; // Dynamic Viewport VkDynamicState state[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamicInfo { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .dynamicStateCount = 2, .pDynamicStates = &state[0] }; // Build the pipeline // Uses all the class members info structs VkGraphicsPipelineCreateInfo pipelineInfo { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = &_renderInfo, .stageCount = (uint32_t)_shaderStages.size(), .pStages = _shaderStages.data(), .pVertexInputState = &_vertexInputInfo, .pInputAssemblyState = &_inputAssembly, .pViewportState = &viewportState, .pRasterizationState = &_rasterizer, .pMultisampleState = &_multisampling, .pDepthStencilState = &_depthStencil, .pColorBlendState = &colorBlending, .pDynamicState = &dynamicInfo, .layout = _pipelineLayout }; // Check the creation of the pipeline // I don't use the VK_CHECK() // 'cause there might be pipeline-specific options VkPipeline newPipeline; if( vkCreateGraphicsPipelines( device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &newPipeline ) != VK_SUCCESS ) { fmt::println("[PipelineBuilder] Failed to create pipeline !"); return VK_NULL_HANDLE; // Graphics pipeline failed to be created } else { return newPipeline; } } void PipelineBuilder::setShaders( VkShaderModule vertexShader, VkShaderModule fragmentShader ) { _shaderStages.clear(); _shaderStages.push_back( vkinit::pipelineShaderStageCreateInfo( VK_SHADER_STAGE_VERTEX_BIT, vertexShader ) ); _shaderStages.push_back( vkinit::pipelineShaderStageCreateInfo( VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShader ) ); } #endif #endif