#include "XCEngine/RHI/Vulkan/VulkanPipelineState.h" #include "XCEngine/RHI/Vulkan/VulkanDevice.h" #include #include #include #include #include #include namespace XCEngine { namespace RHI { namespace { std::string NarrowAscii(const std::wstring& value) { std::string result; result.reserve(value.size()); for (wchar_t ch : value) { result.push_back(static_cast(ch)); } return result; } bool LoadSpirvBytes(const ShaderCompileDesc& desc, std::vector& words, std::string& entryPoint) { entryPoint = NarrowAscii(desc.entryPoint); if (entryPoint.empty()) { entryPoint = "main"; } if (desc.sourceLanguage != ShaderLanguage::SPIRV) { return false; } std::vector bytes; if (!desc.source.empty()) { bytes.assign(desc.source.begin(), desc.source.end()); } else if (!desc.fileName.empty()) { std::ifstream file(std::filesystem::path(desc.fileName), std::ios::binary | std::ios::ate); if (!file.is_open()) { return false; } const std::streamsize fileSize = file.tellg(); if (fileSize <= 0 || (fileSize % 4) != 0) { return false; } bytes.resize(static_cast(fileSize)); file.seekg(0, std::ios::beg); if (!file.read(bytes.data(), fileSize)) { return false; } } else { return false; } if ((bytes.size() % sizeof(uint32_t)) != 0) { return false; } words.resize(bytes.size() / sizeof(uint32_t)); std::memcpy(words.data(), bytes.data(), bytes.size()); return !words.empty(); } VkShaderModule CreateShaderModule(VkDevice device, const std::vector& words) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = words.size() * sizeof(uint32_t); createInfo.pCode = words.data(); VkShaderModule module = VK_NULL_HANDLE; if (vkCreateShaderModule(device, &createInfo, nullptr, &module) != VK_SUCCESS) { return VK_NULL_HANDLE; } return module; } } // namespace VulkanPipelineState::~VulkanPipelineState() { Shutdown(); } bool VulkanPipelineState::Initialize(VulkanDevice* device, const GraphicsPipelineDesc& desc) { if (device == nullptr || device->GetDevice() == VK_NULL_HANDLE) { return false; } m_deviceOwner = device; m_device = device->GetDevice(); m_inputLayoutDesc = desc.inputLayout; m_rasterizerDesc = desc.rasterizerState; m_blendDesc = desc.blendState; m_depthStencilDesc = desc.depthStencilState; m_topologyType = desc.topologyType; m_renderTargetCount = desc.renderTargetCount; m_depthStencilFormat = desc.depthStencilFormat; m_sampleCount = desc.sampleCount > 0 ? desc.sampleCount : 1; for (uint32_t i = 0; i < 8; ++i) { m_renderTargetFormats[i] = desc.renderTargetFormats[i]; } if (m_renderTargetCount != 1 || m_renderTargetFormats[0] == 0) { return false; } std::vector vertexWords; std::vector fragmentWords; std::string vertexEntryPoint; std::string fragmentEntryPoint; if (!LoadSpirvBytes(desc.vertexShader, vertexWords, vertexEntryPoint) || !LoadSpirvBytes(desc.fragmentShader, fragmentWords, fragmentEntryPoint)) { return false; } const VkShaderModule vertexModule = CreateShaderModule(m_device, vertexWords); const VkShaderModule fragmentModule = CreateShaderModule(m_device, fragmentWords); if (vertexModule == VK_NULL_HANDLE || fragmentModule == VK_NULL_HANDLE) { if (vertexModule != VK_NULL_HANDLE) { vkDestroyShaderModule(m_device, vertexModule, nullptr); } if (fragmentModule != VK_NULL_HANDLE) { vkDestroyShaderModule(m_device, fragmentModule, nullptr); } return false; } VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; if (vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &m_pipelineLayout) != VK_SUCCESS) { vkDestroyShaderModule(m_device, fragmentModule, nullptr); vkDestroyShaderModule(m_device, vertexModule, nullptr); return false; } VkAttachmentDescription colorAttachment = {}; colorAttachment.format = ToVulkanFormat(static_cast(m_renderTargetFormats[0])); colorAttachment.samples = ToVulkanSampleCount(m_sampleCount); colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; colorAttachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference colorAttachmentRef = {}; colorAttachmentRef.attachment = 0; colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkSubpassDescription subpass = {}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorAttachmentRef; VkRenderPassCreateInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassInfo.attachmentCount = 1; renderPassInfo.pAttachments = &colorAttachment; renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; if (vkCreateRenderPass(m_device, &renderPassInfo, nullptr, &m_renderPass) != VK_SUCCESS) { vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr); m_pipelineLayout = VK_NULL_HANDLE; vkDestroyShaderModule(m_device, fragmentModule, nullptr); vkDestroyShaderModule(m_device, vertexModule, nullptr); return false; } std::map strideBySlot; for (const InputElementDesc& element : m_inputLayoutDesc.elements) { const uint32_t attributeSize = GetFormatSize(static_cast(element.format)); strideBySlot[element.inputSlot] = (std::max)(strideBySlot[element.inputSlot], element.alignedByteOffset + attributeSize); } std::vector bindings; bindings.reserve(strideBySlot.size()); for (const auto& entry : strideBySlot) { VkVertexInputBindingDescription binding = {}; binding.binding = entry.first; binding.stride = entry.second; binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; bindings.push_back(binding); } std::vector attributes; attributes.reserve(m_inputLayoutDesc.elements.size()); for (uint32_t location = 0; location < m_inputLayoutDesc.elements.size(); ++location) { const InputElementDesc& element = m_inputLayoutDesc.elements[location]; VkVertexInputAttributeDescription attribute = {}; attribute.location = location; attribute.binding = element.inputSlot; attribute.format = ToVulkanFormat(static_cast(element.format)); attribute.offset = element.alignedByteOffset; attributes.push_back(attribute); } VkPipelineShaderStageCreateInfo shaderStages[2] = {}; shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; shaderStages[0].module = vertexModule; shaderStages[0].pName = vertexEntryPoint.c_str(); shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; shaderStages[1].module = fragmentModule; shaderStages[1].pName = fragmentEntryPoint.c_str(); VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertexInputInfo.vertexBindingDescriptionCount = static_cast(bindings.size()); vertexInputInfo.pVertexBindingDescriptions = bindings.data(); vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributes.size()); vertexInputInfo.pVertexAttributeDescriptions = attributes.data(); VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; inputAssembly.topology = ToVulkanPrimitiveTopology(static_cast(m_topologyType)); inputAssembly.primitiveRestartEnable = VK_FALSE; VkPipelineViewportStateCreateInfo viewportState = {}; viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewportState.viewportCount = 1; viewportState.scissorCount = 1; VkPipelineRasterizationStateCreateInfo rasterizer = {}; rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer.depthClampEnable = m_rasterizerDesc.depthClipEnable ? VK_FALSE : VK_TRUE; rasterizer.rasterizerDiscardEnable = VK_FALSE; rasterizer.polygonMode = ToVulkanPolygonMode(static_cast(m_rasterizerDesc.fillMode)); rasterizer.lineWidth = 1.0f; rasterizer.cullMode = ToVulkanCullMode(static_cast(m_rasterizerDesc.cullMode)); rasterizer.frontFace = ToVulkanFrontFace(static_cast(m_rasterizerDesc.frontFace)); rasterizer.depthBiasEnable = m_rasterizerDesc.depthBias != 0 || m_rasterizerDesc.slopeScaledDepthBias != 0.0f; rasterizer.depthBiasConstantFactor = static_cast(m_rasterizerDesc.depthBias); rasterizer.depthBiasClamp = m_rasterizerDesc.depthBiasClamp; rasterizer.depthBiasSlopeFactor = m_rasterizerDesc.slopeScaledDepthBias; VkPipelineMultisampleStateCreateInfo multisampling = {}; multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling.rasterizationSamples = ToVulkanSampleCount(m_sampleCount); multisampling.sampleShadingEnable = VK_FALSE; VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; colorBlendAttachment.blendEnable = m_blendDesc.blendEnable ? VK_TRUE : VK_FALSE; colorBlendAttachment.srcColorBlendFactor = ToVulkanBlendFactor(static_cast(m_blendDesc.srcBlend)); colorBlendAttachment.dstColorBlendFactor = ToVulkanBlendFactor(static_cast(m_blendDesc.dstBlend)); colorBlendAttachment.colorBlendOp = ToVulkanBlendOp(static_cast(m_blendDesc.blendOp)); colorBlendAttachment.srcAlphaBlendFactor = ToVulkanBlendFactor(static_cast(m_blendDesc.srcBlendAlpha)); colorBlendAttachment.dstAlphaBlendFactor = ToVulkanBlendFactor(static_cast(m_blendDesc.dstBlendAlpha)); colorBlendAttachment.alphaBlendOp = ToVulkanBlendOp(static_cast(m_blendDesc.blendOpAlpha)); VkPipelineColorBlendStateCreateInfo colorBlending = {}; colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; colorBlending.logicOpEnable = VK_FALSE; colorBlending.attachmentCount = 1; colorBlending.pAttachments = &colorBlendAttachment; VkPipelineDepthStencilStateCreateInfo depthStencil = {}; depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; depthStencil.depthTestEnable = m_depthStencilDesc.depthTestEnable ? VK_TRUE : VK_FALSE; depthStencil.depthWriteEnable = m_depthStencilDesc.depthWriteEnable ? VK_TRUE : VK_FALSE; depthStencil.depthCompareOp = ToVulkanCompareOp(static_cast(m_depthStencilDesc.depthFunc)); depthStencil.depthBoundsTestEnable = m_depthStencilDesc.depthBoundsEnable ? VK_TRUE : VK_FALSE; depthStencil.stencilTestEnable = m_depthStencilDesc.stencilEnable ? VK_TRUE : VK_FALSE; VkDynamicState dynamicStates[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamicState = {}; dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamicState.dynamicStateCount = static_cast(std::size(dynamicStates)); dynamicState.pDynamicStates = dynamicStates; VkGraphicsPipelineCreateInfo pipelineInfo = {}; pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipelineInfo.stageCount = 2; pipelineInfo.pStages = shaderStages; pipelineInfo.pVertexInputState = &vertexInputInfo; pipelineInfo.pInputAssemblyState = &inputAssembly; pipelineInfo.pViewportState = &viewportState; pipelineInfo.pRasterizationState = &rasterizer; pipelineInfo.pMultisampleState = &multisampling; pipelineInfo.pDepthStencilState = &depthStencil; pipelineInfo.pColorBlendState = &colorBlending; pipelineInfo.pDynamicState = &dynamicState; pipelineInfo.layout = m_pipelineLayout; pipelineInfo.renderPass = m_renderPass; pipelineInfo.subpass = 0; const bool success = vkCreateGraphicsPipelines( m_device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &m_pipeline) == VK_SUCCESS; vkDestroyShaderModule(m_device, fragmentModule, nullptr); vkDestroyShaderModule(m_device, vertexModule, nullptr); if (!success) { Shutdown(); return false; } return true; } void VulkanPipelineState::SetInputLayout(const InputLayoutDesc& layout) { m_inputLayoutDesc = layout; } void VulkanPipelineState::SetRasterizerState(const RasterizerDesc& state) { m_rasterizerDesc = state; } void VulkanPipelineState::SetBlendState(const BlendDesc& state) { m_blendDesc = state; } void VulkanPipelineState::SetDepthStencilState(const DepthStencilStateDesc& state) { m_depthStencilDesc = state; } void VulkanPipelineState::SetTopology(uint32_t topologyType) { m_topologyType = topologyType; } void VulkanPipelineState::SetRenderTargetFormats(uint32_t count, const uint32_t* formats, uint32_t depthFormat) { m_renderTargetCount = count; m_depthStencilFormat = depthFormat; for (uint32_t i = 0; i < count && i < 8; ++i) { m_renderTargetFormats[i] = formats[i]; } } void VulkanPipelineState::SetSampleCount(uint32_t count) { m_sampleCount = count > 0 ? count : 1; } void VulkanPipelineState::SetComputeShader(RHIShader* shader) { (void)shader; } PipelineStateHash VulkanPipelineState::GetHash() const { PipelineStateHash hash = {}; hash.topologyHash = m_topologyType; hash.renderTargetHash = m_renderTargetCount ^ (m_renderTargetFormats[0] << 8); return hash; } void VulkanPipelineState::Shutdown() { if (m_pipeline != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) { vkDestroyPipeline(m_device, m_pipeline, nullptr); m_pipeline = VK_NULL_HANDLE; } if (m_renderPass != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) { vkDestroyRenderPass(m_device, m_renderPass, nullptr); m_renderPass = VK_NULL_HANDLE; } if (m_pipelineLayout != VK_NULL_HANDLE && m_device != VK_NULL_HANDLE) { vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr); m_pipelineLayout = VK_NULL_HANDLE; } m_deviceOwner = nullptr; m_device = VK_NULL_HANDLE; } } // namespace RHI } // namespace XCEngine