Criação do pipeline gráfico
Agora que temos todos os recursos necessários para criar o pipeline gráfico, podemos começar a preencher a estrutura VkGraphicsPipelineCreateInfo
no final da função initPipeline()
, mas antes das chamadas para vkDestroyShaderModule
porque os módulos de shader ainda devem ser usados durante a criação.
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = shaderStages;
Começamos referenciando o array de estruturas VkPipelineShaderStageCreateInfo
.
pipelineInfo.pDynamicState = &dynamicInfo;
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssemblyInfo;
pipelineInfo.pRasterizationState = &rasterizationInfo;
pipelineInfo.pColorBlendState = &colorBlending;
Em seguida, referenciamos todas as estruturas que descrevem o estágio de função fixa.
pipelineInfo.layout = m_pipelineLayout;
Depois disso, vem o layout do pipeline, que é um identificador do Vulkan em vez de um ponteiro para uma estrutura.
pipelineInfo.renderPass = m_window->defaultRenderPass();
pipelineInfo.subpass = 0;
Por último, temos a referência para o render pass (que já foi criado automaticamente pela classe QVulkanWindow
) o e o índice do subpass onde este pipeline gráfico será usado8. O render pass criado pela classe QVulkanWindow
possuí apenas um subpass, portando, definimos o valor do campo subpass
como 0
.
8 Também podemos usar o mesmo pipeline com outros render passes se eles forem compatíveis com o especificado, mas não usaremos esse recurso neste projeto.
Agora nos preparemos para a etapa final criando um membro de classe para manter o objeto VkPipeline
:
VkPipeline m_graphicsPipeline = nullptr;
E finalmente criamos o pipeline gráfico chamando a função vkCreateGraphicsPipelines
:
result = m_deviceFunctions->vkCreateGraphicsPipelines(
device,
VK_NULL_HANDLE,
1,
&pipelineInfo,
nullptr,
&m_graphicsPipeline
);
if (result != VK_SUCCESS)
qFatal("Failed to graphics pipeline: %d", result);
Essa função na verdade tem mais parâmetros que as funções comuns de criação de objetos no Vulkan. Ela é projetada para receber vários objetos VkGraphicsPipelineCreateInfo
e criar vários objetos VkPipeline
em uma única chamada. O segundo parâmetro, para o qual passamos o argumento VK_NULL_HANDLE
, faz referência a um objeto VkPipelineCache
opcional.
O pipeline gráfico é necessário para todas as operações comuns de desenho, portanto, ele também deve ser destruído apenas no final do programa:
void Renderer::releaseResources()
{
VkDevice device = m_window->device();
m_deviceFunctions->vkDestroyPipeline(device, m_graphicsPipeline, nullptr);
m_deviceFunctions->vkDestroyPipelineLayout(device, m_pipelineLayout, nullptr);
}
Agora, podemos executar nosso programa para confirmar que todo esse trabalho resultou em uma criação de pipeline bem-sucedida. Já estamos chegando perto de ver algo aparecer na tela. Na próxima seção, vamos iniciar o render pass.